diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 90122d6..b4493b2 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -13,13 +13,12 @@ jobs: strategy: matrix: - node-version: [21.x, 22.x] - # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ - + node-version: [22, 24] + steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - run: npm install diff --git a/package.json b/package.json index a70dc50..e5652ae 100644 --- a/package.json +++ b/package.json @@ -1,23 +1,25 @@ { - "name": "@garmin/fitsdk", - "version": "21.202.0", - "description": "FIT JavaScript SDK", - "main": "src/index.js", - "type": "module", - "scripts": { - "build": "node .", - "test": "vitest run" - }, - "author": "Garmin International, Inc.", - "license": "SEE LICENSE IN LICENSE.txt", - "repository": { - "type": "git", - "url": "https://github.com/garmin/fit-javascript-sdk" - }, - "files": [ - "src/" - ], - "devDependencies": { - "vitest": "^2.1.8" - } -} + "name": "@garmin/fitsdk", + "version": "21.205.0", + "description": "FIT JavaScript SDK", + "main": "src/index.js", + "types": "src/index.d.ts", + "type": "module", + "scripts": { + "test": "vitest run --silent=passed-only --typecheck" + }, + "author": "Garmin International, Inc.", + "license": "SEE LICENSE IN LICENSE.txt", + "repository": { + "type": "git", + "url": "https://github.com/garmin/fit-javascript-sdk" + }, + "files": [ + "src/" + ], + "devDependencies": { + "@types/node": "^22.0.0", + "typescript": "^5.0.0", + "vitest": "^4.1.5" + } +} \ No newline at end of file diff --git a/src/accumulator.js b/src/accumulator.js index 1d8d980..d7ce946 100644 --- a/src/accumulator.js +++ b/src/accumulator.js @@ -5,8 +5,8 @@ // Transfer (FIT) Protocol License. ///////////////////////////////////////////////////////////////////////////////////////////// // ****WARNING**** This file is auto-generated! Do NOT edit this file. -// Profile Version = 21.202.0Release -// Tag = production/release/21.202.0-0-g9a57aebc +// Profile Version = 21.205.0Release +// Tag = production/release/21.205.0-0-gb3c261eb ///////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/bit-stream.js b/src/bit-stream.js index cbf0e02..101a99d 100644 --- a/src/bit-stream.js +++ b/src/bit-stream.js @@ -5,25 +5,28 @@ // Transfer (FIT) Protocol License. ///////////////////////////////////////////////////////////////////////////////////////////// // ****WARNING**** This file is auto-generated! Do NOT edit this file. -// Profile Version = 21.202.0Release -// Tag = production/release/21.202.0-0-g9a57aebc +// Profile Version = 21.205.0Release +// Tag = production/release/21.205.0-0-gb3c261eb ///////////////////////////////////////////////////////////////////////////////////////////// import FIT from "./fit.js"; class BitStream { + #data = null; #array = null; #currentArrayPosition = 0; #bitPerPosition = 0; + #isBigInt = false; #currentByte = 0; #currentBit = 0; #bitsAvailable = 0; constructor(data, baseType = FIT.BaseType.UINT8) { - this.#array = Array.isArray(data) ? data : [data]; + this.#data = Array.isArray(data) ? data : [data]; const baseTypeSize = FIT.BaseTypeDefinitions[baseType].size; this.#bitPerPosition = baseTypeSize * 8; + this.#isBigInt = this.#bitPerPosition > 32; this.reset(); } @@ -36,9 +39,11 @@ class BitStream { } reset() { + this.#array = null; this.#currentArrayPosition = 0; - this.#bitsAvailable = this.#bitPerPosition * this.#array.length; - this.#nextByte(); + this.#bitsAvailable = this.#bitPerPosition * this.#data.length; + this.#currentByte = 0; + this.#currentBit = 0; } readBit() { @@ -46,12 +51,24 @@ class BitStream { this.#throwError(); } + if (this.#array === null) { + this.#array = this.#data; + this.#currentArrayPosition = this.#data.length - (this.#bitsAvailable / this.#bitPerPosition); + this.#nextByte(); + } + if (this.#currentBit >= this.#bitPerPosition) { this.#nextByte(); } - const bit = this.#currentByte & 0x01; - this.#currentByte = this.#currentByte >> 1; + let bit; + if (this.#isBigInt) { + bit = Number(this.#currentByte & 1n); + this.#currentByte >>= 1n; + } else { + bit = this.#currentByte & 0x01; + this.#currentByte >>= 1; + } this.#currentBit++; this.#bitsAvailable--; @@ -59,10 +76,36 @@ class BitStream { } readBits(nBitsToRead) { + // Fast path: reading exactly one value's worth of bits and #array not yet initialized + if (nBitsToRead === this.#bitPerPosition && this.#array === null) { + if (!this.hasBitsAvailable) { + this.#throwError(); + } + const index = this.#data.length - (this.#bitsAvailable / this.#bitPerPosition); + const value = this.#data[index]; + this.#bitsAvailable -= nBitsToRead; + + if (this.#isBigInt) { + return Number(BigInt.asUintN(nBitsToRead, value)); + } + if (nBitsToRead < 32) { + return (value & ((1 << nBitsToRead) - 1)) >>> 0; + } + return value >>> 0; + } + + if (nBitsToRead <= 32) { + let value = 0; + for (let i = 0; i < nBitsToRead; i++) { + value |= this.readBit() << i; + } + return value >>> 0; + } + let value = 0n; - for (let i = 0n; i < nBitsToRead; i++) { - value |= BigInt(this.readBit()) << i; + for (let i = 0, shift = 0n; i < nBitsToRead; i++, shift++) { + value |= BigInt(this.readBit()) << shift; } return Number(value); @@ -77,6 +120,10 @@ class BitStream { this.#currentBit = 0; } + get array() { + return this.#array; + } + #throwError(error = "") { throw Error("FIT Runtime Error no bits available."); } diff --git a/src/crc-calculator.js b/src/crc-calculator.js index 640c408..037198e 100644 --- a/src/crc-calculator.js +++ b/src/crc-calculator.js @@ -5,8 +5,8 @@ // Transfer (FIT) Protocol License. ///////////////////////////////////////////////////////////////////////////////////////////// // ****WARNING**** This file is auto-generated! Do NOT edit this file. -// Profile Version = 21.202.0Release -// Tag = production/release/21.202.0-0-g9a57aebc +// Profile Version = 21.205.0Release +// Tag = production/release/21.205.0-0-gb3c261eb ///////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/decoder.js b/src/decoder.js index a4d574f..eeaaabc 100644 --- a/src/decoder.js +++ b/src/decoder.js @@ -5,8 +5,8 @@ // Transfer (FIT) Protocol License. ///////////////////////////////////////////////////////////////////////////////////////////// // ****WARNING**** This file is auto-generated! Do NOT edit this file. -// Profile Version = 21.202.0Release -// Tag = production/release/21.202.0-0-g9a57aebc +// Profile Version = 21.205.0Release +// Tag = production/release/21.205.0-0-gb3c261eb ///////////////////////////////////////////////////////////////////////////////////////////// @@ -61,12 +61,8 @@ class Decoder { #optIncludeUnknownData = false; #optMergeHeartRates = true; #optDecodeMemoGlobs = false; + #optLegacyArrayMode = false; - /** - * Creates a FIT File Decoder - * @constructor - * @param {Stream} stream - representing the FIT file to decode - */ constructor(stream) { if (stream == null) { throw Error("FIT Runtime Error stream parameter is null or undefined"); @@ -75,12 +71,6 @@ class Decoder { this.#stream = stream; } - /** - * Inspects the file header to determine if the input stream is a FIT file - * @param {Stream} stream - * @returns {Boolean} True if the stream is a FIT file - * @static - */ static isFIT(stream) { try { const fileHeaderSize = stream.peekByte(); @@ -104,18 +94,10 @@ class Decoder { return true; } - /** - * Inspects the file header to determine if the input stream is a FIT file - * @returns {Boolean} True if the stream is a FIT file - */ isFIT() { return Decoder.isFIT(this.#stream); } - /** - * Checks that the input stream is a FIT file and verifies both the header and file CRC values - * @returns {Boolean} True if the stream passes the isFit() and CRC checks - */ checkIntegrity() { try { if (!this.isFIT()) { @@ -128,7 +110,7 @@ class Decoder { return false; } - const buf = new Uint8Array(this.#stream.slice(0, this.#stream.length)) + const buf = this.#stream.asUint8Array(); if (fileHeader.headerSize === HEADER_WITH_CRC_SIZE && fileHeader.headerCRC !== 0x0000 && fileHeader.headerCRC != CrcCalculator.calculateCRC(buf, 0, 12)) { @@ -147,48 +129,6 @@ class Decoder { return true; } - /** - * Message Listener Callback - * - * @callback Decoder~mesgListener - * @param {Number} mesgNum - Profile.MesgNum - * @param {Object} message - The message - */ - - /** - * Message Definition Listener Callback - * - * @callback Decoder~mesgDefinitionListener - * @param {Object} messageDefinition - The message Definition - */ - - /** - * Developer Field Description Listener Callback - * - * @callback Decoder~fieldDescriptionListener - * @param {Number} key - The key associated with this pairing of of Developer Data Id and Field Description Mesgs - * @param {Object} developerDataIdMesg - The Developer Data Id Mesg associated with this pairing - * @param {Object} fieldDescriptionMesg - The Field Description Mesg associated with this pairing - */ - - /** - * Read the messages from the stream. - * @param {Object=} [options] - Read options (optional) - * @param {Decoder~mesgListener} [options.mesgListener=null] - (optional, default null) mesgListener(mesgNum, message) - * @param {Decoder~mesgDefinitionListener} [options.mesgDefinitionListener=null] - (optional, default null) mesgDefinitionListener(mesgDefinition) - * @param {Decoder~fieldDescriptionListener} [options.fieldDescriptionListener=null] - (optional, default null) fieldDescriptionListener(key, developerDataIdMesg, fieldDescriptionMesg) - * @param {Boolean} [options.expandSubFields=true] - (optional, default true) - * @param {Boolean} [options.expandComponents=true] - (optional, default true) - * @param {Boolean} [options.applyScaleAndOffset=true] - (optional, default true) - * @param {Boolean} [options.convertTypesToStrings=true] - (optional, default true) - * @param {boolean} [options.convertDateTimesToDates=true] - (optional, default true) - * @param {Boolean} [options.includeUnknownData=false] - (optional, default false) - * @param {boolean} [options.mergeHeartRates=true] - (optional, default true) - * @param {boolean} [options.decodeMemoGlobs=true] - (optional, default false) - * @param {boolean} [options.skipHeader=false] - (optional, default false) - * @param {boolean} [options.dataOnly=false] - (optional, default false) - * @return {Object} result - {messages:Array, errors:Array} - */ read({ mesgListener = null, mesgDefinitionListener = null, @@ -202,7 +142,8 @@ class Decoder { mergeHeartRates = true, decodeMemoGlobs = false, skipHeader = false, - dataOnly = false, } = {}) { + dataOnly = false, + legacyArrayMode = false, } = {}) { this.#mesgListener = mesgListener; this.#mesgDefinitionListener = mesgDefinitionListener; @@ -215,6 +156,7 @@ class Decoder { this.#optIncludeUnknownData = includeUnknownData; this.#optMergeHeartRates = mergeHeartRates; this.#optDecodeMemoGlobs = decodeMemoGlobs; + this.#optLegacyArrayMode = legacyArrayMode; this.#localMessageDefinitions = []; this.#developerDataDefinitions = {}; @@ -224,6 +166,10 @@ class Decoder { const errors = []; try { + if (this.#optLegacyArrayMode) { + console.log("Decoder Warning: legacyArrayMode is deprecated and will be removed in the future. Afterwards, the default behavior will be equivalent to legacyArrayMode=false."); + } + if (this.#optMergeHeartRates && (!this.#optApplyScaleAndOffset || !this.#optExpandComponents)) { this.#throwError("mergeHeartRates requires applyScaleAndOffset and expandComponents to be enabled"); } @@ -233,6 +179,7 @@ class Decoder { } this.#decodeMode = skipHeader ? DecodeMode.SKIP_HEADER : dataOnly ? DecodeMode.DATA_ONLY : DecodeMode.NORMAL; + this.#stream.legacyArrayMode = this.#optLegacyArrayMode; while (this.#stream.position < this.#stream.length) { this.#decodeNextFile(); @@ -304,9 +251,9 @@ class Decoder { messageDefinition["reserved"] = this.#stream.readByte(); messageDefinition["architecture"] = this.#stream.readByte(); - messageDefinition["endianness"] = messageDefinition.architecture === 0 ? Stream.LITTLE_ENDIAN : Stream.BIG_ENDIAN; + messageDefinition["endianness"] = messageDefinition.architecture === 0; - messageDefinition["globalMessageNumber"] = this.#stream.readUInt16({ endianness: messageDefinition["endianness"] }); + messageDefinition["globalMessageNumber"] = this.#stream.readUInt16({ littleEndian: messageDefinition["endianness"] }); messageDefinition["numFields"] = this.#stream.readByte(); messageDefinition["fieldDefinitions"] = []; messageDefinition["developerFieldDefinitions"] = []; @@ -346,7 +293,7 @@ class Decoder { } } - this.#mesgDefinitionListener?.({...messageDefinition}); + this.#mesgDefinitionListener?.({ ...messageDefinition }); let messageProfile = Profile.messages[messageDefinition.globalMessageNumber]; @@ -417,7 +364,7 @@ class Decoder { developerFieldDefinition["invalidValue"] = FIT.BaseTypeDefinitions[developerFieldDefinition.baseType].invalid; developerFieldDefinition["baseTypeSize"] = FIT.BaseTypeDefinitions[developerFieldDefinition.baseType].size; - const { rawFieldValue: fieldValue } = this.#readFieldValue(messageDefinition, developerFieldDefinition, field); + const fieldValue = this.#readDeveloperFieldValue(messageDefinition, developerFieldDefinition, field); if (fieldValue != null) { developerFields[field.key] = fieldValue; @@ -458,7 +405,7 @@ class Decoder { return developerDataIdMesg.developerDataIndex === message.developerDataIndex; }) ?? {}; - this.#fieldDescriptionListener(message.key, {...developerDataIdMesg}, {...message}); + this.#fieldDescriptionListener(message.key, { ...developerDataIdMesg }, { ...message }); } } } @@ -485,10 +432,22 @@ class Decoder { fieldDefinition.baseType, fieldDefinition.size, { - endianness: messageDefinition["endianness"], - convertInvalidToNull: !field?.hasComponents ?? false - } + littleEndian: messageDefinition["endianness"], + convertInvalidToNull: !field?.hasComponents ?? false, + isArray: field?.array ?? fieldDefinition.size > fieldDefinition.baseTypeSize, + }, ); + + return rawFieldValue; + } + + #readDeveloperFieldValue(messageDefinition, developerFieldDefinition, field) { + + // Preserve arrays for developer fields + this.#stream.legacyArrayMode = true; + const rawFieldValue = this.#readRawFieldValue(messageDefinition, developerFieldDefinition, field); + this.#stream.legacyArrayMode = this.#optLegacyArrayMode; + return rawFieldValue; } @@ -570,8 +529,9 @@ class Decoder { continue; } if (referenceField.rawFieldValue === map.value) { - message[subField.name] = JSON.parse(JSON.stringify(message[field.name])); - message[subField.name].isSubField = true; + const src = message[field.name]; + const rawFieldValue = Array.isArray(src.rawFieldValue) ? [...src.rawFieldValue] : src.rawFieldValue; + message[subField.name] = { ...src, rawFieldValue, isSubField: true }; if (subField.hasComponents) { this.#fieldsToExpand.push(subField.name); @@ -625,6 +585,7 @@ class Decoder { rawFieldValue: [], fieldDefinitionNumber: targetField.num, isExpandedField: true, + isArray: targetField?.array ?? false, invalidValue, }; } @@ -663,8 +624,9 @@ class Decoder { } Object.keys(mesg).forEach((key) => { - mesg[key].fieldValue = UtilsInternal.sanitizeValues(mesg[key].fieldValue); - mesg[key].rawFieldValue = UtilsInternal.sanitizeValues(mesg[key].rawFieldValue); + const field = mesg[key]; + mesg[key].fieldValue = UtilsInternal.sanitizeValues(field.fieldValue, { legacyArrayMode: this.#optLegacyArrayMode, isArray: field.isArray }); + mesg[key].rawFieldValue = UtilsInternal.sanitizeValues(field.rawFieldValue, { legacyArrayMode: this.#optLegacyArrayMode, isArray: field.isArray }); message[key] = mesg[key]; }); @@ -693,7 +655,7 @@ class Decoder { if (field == null) { fieldValue = rawFieldValue; } - else if (FIT.NumericFieldTypes.includes(field?.type ?? -1)) { + else if (FIT.NumericFieldTypesSet.has(field?.type ?? -1)) { fieldValue = this.#applyScaleAndOffset(messageDefinition, field, rawFieldValue); } else if (field.type === "string") { @@ -717,7 +679,7 @@ class Decoder { return rawFieldValue; } - if (FIT.NumericFieldTypes.includes(field?.type ?? -1) === false) { + if (FIT.NumericFieldTypesSet.has(field?.type ?? -1) === false) { return rawFieldValue; } @@ -760,7 +722,7 @@ class Decoder { components.forEach((componentFieldNum, i) => { const targetField = messageDefinition.fields[componentFieldNum]; - if(targetField?.num == field.num && targetField?.isAccumulated) { + if (targetField?.num == field.num && targetField?.isAccumulated) { value = (((value / field.scale) - field.offset) + containingField.offset[i]) * containingField.scale[i]; } }); @@ -775,7 +737,7 @@ class Decoder { return rawFieldValue; } - if (FIT.NumericFieldTypes.includes(field?.type ?? -1)) { + if (FIT.NumericFieldTypesSet.has(field?.type ?? -1)) { return rawFieldValue; } diff --git a/src/encoder.js b/src/encoder.js index 79bd02d..f1b69c4 100644 --- a/src/encoder.js +++ b/src/encoder.js @@ -5,8 +5,8 @@ // Transfer (FIT) Protocol License. ///////////////////////////////////////////////////////////////////////////////////////////// // ****WARNING**** This file is auto-generated! Do NOT edit this file. -// Profile Version = 21.202.0Release -// Tag = production/release/21.202.0-0-g9a57aebc +// Profile Version = 21.205.0Release +// Tag = production/release/21.205.0-0-gb3c261eb ///////////////////////////////////////////////////////////////////////////////////////////// @@ -20,17 +20,7 @@ import Utils from "./utils.js"; const HEADER_WITH_CRC_SIZE = 14; const HEADER_WITHOUT_CRC_SIZE = 12; -/** - * A class for encoding FIT files. - * @class - */ class Encoder { - /** - Creates a FIT File Encoder - * @param {Object} [options] - Encoder options (optional) - * @param {Object.} data An array of bytes - * @returns {Stream} A new Stream object - * @static - */ static fromByteArray(data) { const buf = new Uint8Array(data); return this.fromArrayBuffer(buf.buffer); } - /** - * Convenience method for creating a Stream from a Node Buffer - * @param {Buffer} buffer - Node Buffer of bytes - * @returns {Stream} A new Stream object - * @static - */ static fromBuffer(buffer) { const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength); return this.fromArrayBuffer(arrayBuffer); } - /** - * Convenience method for creating a Stream from an ArrayBuffer - * @param {ArrayBuffer} arrayBuffer - An ArrayBuffer of bytes - * @returns {Stream} A new Stream object - * @static - */ static fromArrayBuffer(arrayBuffer) { const stream = new Stream(arrayBuffer); return stream; } - /** - * Creates a Stream containing a FIT file - * @constructor - * @param {ArrayBuffer} stream - ArrayBuffer containing a FIT file - */ constructor(arrayBuffer) { this.#position = 0; this.#arrayBuffer = arrayBuffer; + this.#dataView = new DataView(arrayBuffer); } get length() { @@ -85,6 +62,14 @@ class Stream { this.#crcCalculator = crcCalculator; } + get legacyArrayMode() { + return this.#legacyArrayMode; + } + + set legacyArrayMode(legacyArrayMode) { + this.#legacyArrayMode = legacyArrayMode; + } + reset() { this.seek(0); } @@ -98,9 +83,7 @@ class Stream { } peekByte() { - const arrayBuffer = this.#arrayBuffer.slice(this.#position, this.#position + 1); - const dataView = new DataView(arrayBuffer); - return dataView.getUint8(0); + return this.#dataView.getUint8(this.#position); } readByte() { @@ -160,14 +143,27 @@ class Stream { return this.readValue(FIT.BaseType.FLOAT64, 8, { convertInvalidToNull: false, ...opts }); } - readString(strlen) { - return this.readValue(FIT.BaseType.STRING, strlen); + readString(strlen, opts = {}) { + return this.readValue(FIT.BaseType.STRING, strlen, opts); } - readValue(baseType, size, { endianness = Stream.LITTLE_ENDIAN, convertInvalidToNull = true } = {}) { - const baseTypeSize = FIT.BaseTypeDefinitions[baseType].size; - const baseTypeInvalid = FIT.BaseTypeDefinitions[baseType].invalid; + asUint8Array() { + return new Uint8Array(this.#arrayBuffer); + } + readValue( + baseType, + size, + { + littleEndian = true, + convertInvalidToNull = true, + isArray = false + } = {}) { + const baseTypeDef = FIT.BaseTypeDefinitions[baseType]; + const baseTypeSize = baseTypeDef.size; + const baseTypeInvalid = baseTypeDef.invalid; + + const startPos = this.#position; const bytes = this.readBytes(size); if (size % baseTypeSize !== 0) { @@ -186,73 +182,94 @@ class Stream { return null; } - return strings.length === 1 ? strings[0] : strings; - } + if (this.#legacyArrayMode) { + return strings.length === 1 ? strings[0] : strings; + } - const dataView = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength); - let values = []; + return strings[0]; + } const count = size / baseTypeSize; - for (let i = 0; i < count; i++) { + if (count === 1) { + const value = this.#readSingleValue(baseType, startPos, littleEndian); + + if (convertInvalidToNull && value === baseTypeInvalid) { + return null; + } - switch (baseType) { - case FIT.BaseType.BYTE: - case FIT.BaseType.ENUM: - case FIT.BaseType.UINT8: - case FIT.BaseType.UINT8Z: - values.push(dataView.getUint8(i * baseTypeSize)); - break; - - case FIT.BaseType.SINT8: - values.push(dataView.getInt8(i * baseTypeSize)); - break; - - case FIT.BaseType.UINT16: - case FIT.BaseType.UINT16Z: - values.push(dataView.getUint16(i * baseTypeSize, endianness)); - break; - - case FIT.BaseType.SINT16: - values.push(dataView.getInt16(i * baseTypeSize, endianness)); - break; - - case FIT.BaseType.UINT32: - case FIT.BaseType.UINT32Z: - values.push(dataView.getUint32(i * baseTypeSize, endianness)); - break; - - case FIT.BaseType.SINT32: - values.push(dataView.getInt32(i * baseTypeSize, endianness)); - break; - - case FIT.BaseType.UINT64: - case FIT.BaseType.UINT64Z: - values.push(dataView.getBigUint64(i * baseTypeSize, endianness)); - break; - case FIT.BaseType.SINT64: - values.push(dataView.getBigInt64(i * baseTypeSize, endianness)); - break; - - case FIT.BaseType.FLOAT32: - values.push(dataView.getFloat32(i * baseTypeSize, endianness)); - break; - - case FIT.BaseType.FLOAT64: - values.push(dataView.getFloat64(i * baseTypeSize, endianness)); - break; + if (!this.#legacyArrayMode && !isArray) { + return value; } + + return UtilsInternal.sanitizeValues([value], { legacyArrayMode: this.#legacyArrayMode, isArray }); } - if (baseType === FIT.BaseType.BYTE) { - return UtilsInternal.onlyInvalidValues(values, baseTypeInvalid) ? null : values; + let values = []; + + for (let i = 0; i < count; i++) { + values.push(this.#readSingleValue(baseType, startPos + i * baseTypeSize, littleEndian)); } if (convertInvalidToNull) { - values = values.map(value => value === baseTypeInvalid ? null : value); + if (UtilsInternal.onlyInvalidValues(values, baseTypeInvalid)) { + return null; + } + + if (baseType !== FIT.BaseType.BYTE) { + values = values.map(value => value === baseTypeInvalid ? null : value); + } } - return UtilsInternal.sanitizeValues(values); + return UtilsInternal.sanitizeValues( + values, + { + legacyArrayMode: this.#legacyArrayMode, + isArray + }); + } + + #readSingleValue(baseType, offset, littleEndian) { + switch (baseType) { + case FIT.BaseType.BYTE: + case FIT.BaseType.ENUM: + case FIT.BaseType.UINT8: + case FIT.BaseType.UINT8Z: + return this.#dataView.getUint8(offset); + + case FIT.BaseType.SINT8: + return this.#dataView.getInt8(offset); + + case FIT.BaseType.UINT16: + case FIT.BaseType.UINT16Z: + return this.#dataView.getUint16(offset, littleEndian); + + case FIT.BaseType.SINT16: + return this.#dataView.getInt16(offset, littleEndian); + + case FIT.BaseType.UINT32: + case FIT.BaseType.UINT32Z: + return this.#dataView.getUint32(offset, littleEndian); + + case FIT.BaseType.SINT32: + return this.#dataView.getInt32(offset, littleEndian); + + case FIT.BaseType.UINT64: + case FIT.BaseType.UINT64Z: + return this.#dataView.getBigUint64(offset, littleEndian); + + case FIT.BaseType.SINT64: + return this.#dataView.getBigInt64(offset, littleEndian); + + case FIT.BaseType.FLOAT32: + return this.#dataView.getFloat32(offset, littleEndian); + + case FIT.BaseType.FLOAT64: + return this.#dataView.getFloat64(offset, littleEndian); + + default: + throw new Error(`Unsupported FIT base type: ${baseType}`); + } } } diff --git a/src/types/crc-calculator.d.ts b/src/types/crc-calculator.d.ts new file mode 100644 index 0000000..a4eed08 --- /dev/null +++ b/src/types/crc-calculator.d.ts @@ -0,0 +1,34 @@ +///////////////////////////////////////////////////////////////////////////////////////////// +// Copyright 2026 Garmin International, Inc. +// Licensed under the Flexible and Interoperable Data Transfer (FIT) Protocol License; you +// may not use this file except in compliance with the Flexible and Interoperable Data +// Transfer (FIT) Protocol License. +///////////////////////////////////////////////////////////////////////////////////////////// +// ****WARNING**** This file is auto-generated! Do NOT edit this file. +// Profile Version = 21.205.0Release +// Tag = production/release/21.205.0-0-gb3c261eb +///////////////////////////////////////////////////////////////////////////////////////////// + + +/** Computes the 16-bit CRC used to validate FIT file headers and file data. */ +export class CrcCalculator { + constructor(); + /** The current running CRC value. */ + readonly crc: number; + /** + * Feeds a range of bytes into the running CRC. + * @param buf - The byte array to process. + * @param start - Inclusive start index within `buf`. + * @param end - Exclusive end index within `buf`. + * @returns The updated CRC value. + */ + addBytes(buf: Uint8Array, start: number, end: number): number; + /** + * Computes the CRC for a range of bytes without requiring an instance. + * @param buf - The byte array to process. + * @param start - Inclusive start index within `buf`. + * @param end - Exclusive end index within `buf`. + * @returns The computed CRC value. + */ + static calculateCRC(buf: Uint8Array, start: number, end: number): number; +} diff --git a/src/types/decoder.d.ts b/src/types/decoder.d.ts new file mode 100644 index 0000000..627114f --- /dev/null +++ b/src/types/decoder.d.ts @@ -0,0 +1,140 @@ +///////////////////////////////////////////////////////////////////////////////////////////// +// Copyright 2026 Garmin International, Inc. +// Licensed under the Flexible and Interoperable Data Transfer (FIT) Protocol License; you +// may not use this file except in compliance with the Flexible and Interoperable Data +// Transfer (FIT) Protocol License. +///////////////////////////////////////////////////////////////////////////////////////////// +// ****WARNING**** This file is auto-generated! Do NOT edit this file. +// Profile Version = 21.205.0Release +// Tag = production/release/21.205.0-0-gb3c261eb +///////////////////////////////////////////////////////////////////////////////////////////// + + +import { Stream } from "./stream"; +import { Mesg } from "./mesg"; +import { FitMessages, DeveloperDataIdMesg, FieldDescriptionMesg } from "./mesgs"; + +/** Decodes FIT binary data from a {@link Stream}. */ +export class Decoder { + /** + * Creates a FIT File Decoder. + * @param stream - Stream representing the FIT file to decode. + */ + constructor(stream: Stream); + + /** + * Inspects the file header to determine if the input stream is a FIT file. + * @param stream - The stream to inspect. + * @returns `true` if the stream is a FIT file. + */ + static isFIT(stream: Stream): boolean; + + /** + * Inspects the file header to determine if the input stream is a FIT file. + * @returns `true` if the stream is a FIT file. + */ + isFIT(): boolean; + + /** + * Checks that the input stream is a FIT file and verifies both the header and file CRC values. + * @returns `true` if the stream passes the {@link isFIT} and CRC checks. + */ + checkIntegrity(): boolean; + + /** + * Read the messages from the stream. + * @param options - Read options. See {@link DecoderOptions}. + * @returns Object containing decoded messages, profile version, and any errors. + */ + read(options?: DecoderOptions): { messages: FitMessages; profileVersion: ProfileVersion; errors: Error[] }; +} + +/** Describes a single field within a MesgDefinition. */ +export interface FieldDefinition { + /** Field definition number identifying the field within its message. */ + fieldDefinitionNumber: number; + /** Size in bytes of the field. */ + size: number; + /** FIT base type identifier for this field's values. */ + baseType: number; + /** The invalid value for this field's base type. */ + invalidValue: number; + /** Byte size of a single element of this field's base type. */ + baseTypeSize: number; +} + +/** Describes a single developer-defined field within a raw FIT message definition record. */ +export interface DeveloperFieldDefinition { + /** Developer field definition number, scoped to the developer data index. */ + fieldDefinitionNumber: number; + /** Size in bytes of the field. */ + size: number; + /** Index identifying the developer application that defined this field. */ + developerDataIndex: number; +} + +/** The definition record for a FIT message*/ +export interface MesgDefinition { + /** Raw record header byte. */ + recordHeader: number; + /** Local message number used to map messages to a definition. */ + localMesgNum: number; + /** Reserved byte from the definition record. */ + reserved: number; + /** Architecture byte: `0` = little-endian, `1` = big-endian. */ + architecture: number; + /** Endianness derived from `architecture` (`true` == little-endian or `false` == big-endian). */ + endianness: boolean; + /** Global message number identifying the message type in the FIT profile. */ + globalMessageNumber: number; + /** Number of fields in the message. */ + numFields: number; + /** Array of field definitions for the message. */ + fieldDefinitions: FieldDefinition[]; + /** Developer field definitions for the message. */ + developerFieldDefinitions: DeveloperFieldDefinition[]; + /** Total size in bytes of the combined fields. */ + messageSize: number; + /** Total size in bytes of the combined developer fields. */ + developerDataSize: number; +} + +/** Options controlling how {@link Decoder.read} decodes the FIT stream. */ +export interface DecoderOptions { + /** Callback invoked for each decoded message. */ + mesgListener?: (mesgNum: number, mesg: Mesg) => void; + /** Callback invoked for each decoded message definition. */ + mesgDefinitionListener?: (mesgDefinition: MesgDefinition) => void; + /** Callback invoked for each developer field description. */ + fieldDescriptionListener?: (key: number, developerDataIdMesg: DeveloperDataIdMesg, fieldDescriptionMesg: FieldDescriptionMesg) => void; + /** Expand sub fields. Default: `true`. */ + expandSubFields?: boolean; + /** Expand component fields. Default: `true`. */ + expandComponents?: boolean; + /** Apply scale and offset to numeric fields. Default: `true`. */ + applyScaleAndOffset?: boolean; + /** Convert type values to their string representations. Default: `true`. */ + convertTypesToStrings?: boolean; + /** Convert FIT epoch timestamps to `Date` objects. Default: `true`. */ + convertDateTimesToDates?: boolean; + /** Include fields not in the profile. Default: `false`. */ + includeUnknownData?: boolean; + /** Merge HR messages into record messages. Requires `applyScaleAndOffset` and `expandComponents`. Default: `true`. */ + mergeHeartRates?: boolean; + /** Decode memo glob messages. Default: `false`. */ + decodeMemoGlobs?: boolean; + /** Skip the FIT file header. Default: `false`. */ + skipHeader?: boolean; + /** Decode data bytes only, no header or CRC. Default: `false`. */ + dataOnly?: boolean; + /** @deprecated Default: `false`. */ + legacyArrayMode?: boolean; +} + +/** The FIT profile version embedded in a FIT file header. */ +export interface ProfileVersion { + /** Major version number. */ + major: number; + /** Minor version number (tenths). */ + minor: number; +} diff --git a/src/types/encoder.d.ts b/src/types/encoder.d.ts new file mode 100644 index 0000000..b0f4ddb --- /dev/null +++ b/src/types/encoder.d.ts @@ -0,0 +1,69 @@ +///////////////////////////////////////////////////////////////////////////////////////////// +// Copyright 2026 Garmin International, Inc. +// Licensed under the Flexible and Interoperable Data Transfer (FIT) Protocol License; you +// may not use this file except in compliance with the Flexible and Interoperable Data +// Transfer (FIT) Protocol License. +///////////////////////////////////////////////////////////////////////////////////////////// +// ****WARNING**** This file is auto-generated! Do NOT edit this file. +// Profile Version = 21.205.0Release +// Tag = production/release/21.205.0-0-gb3c261eb +///////////////////////////////////////////////////////////////////////////////////////////// + + +import { Encodable, Mesg } from "./mesg"; +import { DeveloperDataIdMesg, FieldDescriptionMesg } from "./mesgs"; + +export interface FieldDescription { + /** The Developer Data Id message. */ + developerDataIdMesg: DeveloperDataIdMesg; + /** The Field Description message. */ + fieldDescriptionMesg: FieldDescriptionMesg; +} + +/** Options for constructing an {@link Encoder}. */ +export interface EncoderOptions { + /** Developer field descriptions keyed by developer data index. Equivalent to calling {@link Encoder.addDeveloperField} for each entry. */ + fieldDescriptions?: Record; +} + +/** Encodes FIT messages into a binary FIT file. */ +export class Encoder { + /** + * Creates a FIT File Encoder. + * @param options - Encoder options. See {@link EncoderOptions}. + */ + constructor(options?: EncoderOptions); + + /** + * Closes the encoder and returns the encoded file data. + * @returns A `Uint8Array` containing the encoded FIT file. + */ + close(): Uint8Array; + + /** + * Encodes a message into the file. + * @param mesg - The message data, must include a `mesgNum` property. + * @returns `this`. + */ + writeMesg(mesg: Encodable): this; + + /** + * Encodes a message into the file. + * Can be used directly as a {@link DecoderOptions.mesgListener} callback. + * @param mesgNum - The message number. + * @param mesg - The message data. + * @returns `this`. + */ + onMesg(mesgNum: number, mesg: Mesg): this; + + /** + * Adds a Developer Data Field Description and associated Developer Data Id Message to the Encoder. + * Does not write these messages to the output stream. + * Can be used directly as a {@link DecoderOptions.fieldDescriptionListener} callback. + * @param key - The key associated with this developer field pairing. + * @param developerDataIdMesg - The Developer Data Id message. + * @param fieldDescriptionMesg - The Field Description message. + * @returns `this`. + */ + addDeveloperField(key: number, developerDataIdMesg: DeveloperDataIdMesg, fieldDescriptionMesg: FieldDescriptionMesg): this; +} diff --git a/src/types/mesg.d.ts b/src/types/mesg.d.ts new file mode 100644 index 0000000..495c97e --- /dev/null +++ b/src/types/mesg.d.ts @@ -0,0 +1,35 @@ +///////////////////////////////////////////////////////////////////////////////////////////// +// Copyright 2026 Garmin International, Inc. +// Licensed under the Flexible and Interoperable Data Transfer (FIT) Protocol License; you +// may not use this file except in compliance with the Flexible and Interoperable Data +// Transfer (FIT) Protocol License. +///////////////////////////////////////////////////////////////////////////////////////////// +// ****WARNING**** This file is auto-generated! Do NOT edit this file. +// Profile Version = 21.205.0Release +// Tag = production/release/21.205.0-0-gb3c261eb +///////////////////////////////////////////////////////////////////////////////////////////// + + +import Types from "./types"; + +/** A FIT field value — a scalar or array of one of the primitive FIT base types. */ +export type FieldValue = + | string + | number + | boolean + | bigint + | Date + | (string | number | boolean | bigint | Date)[]; + +/** Developer-defined fields keyed by developer field key. */ +export type DeveloperFields = { [developerFieldKey: string]: FieldValue | undefined }; + +/** Base interface for all decoded FIT messages. */ +export interface Mesg { + /** Global FIT message number identifying the message type. */ + mesgNum?: Types.MesgNum; + /** Developer-defined field values attached to this message. */ + developerFields?: DeveloperFields; +} + +export type Encodable = T & Required>; diff --git a/src/types/mesgs.d.ts b/src/types/mesgs.d.ts new file mode 100644 index 0000000..5422240 --- /dev/null +++ b/src/types/mesgs.d.ts @@ -0,0 +1,2638 @@ +///////////////////////////////////////////////////////////////////////////////////////////// +// Copyright 2026 Garmin International, Inc. +// Licensed under the Flexible and Interoperable Data Transfer (FIT) Protocol License; you +// may not use this file except in compliance with the Flexible and Interoperable Data +// Transfer (FIT) Protocol License. +///////////////////////////////////////////////////////////////////////////////////////////// +// ****WARNING**** This file is auto-generated! Do NOT edit this file. +// Profile Version = 21.205.0Release +// Tag = production/release/21.205.0-0-gb3c261eb +///////////////////////////////////////////////////////////////////////////////////////////// + + +import { Mesg } from "./mesg"; +import Types from "./types"; + + +/** + * Must be first message in file. + */ +export interface FileIdMesg extends Mesg { + type?: Types.File; + manufacturer?: Types.Manufacturer; + product?: Types.Uint16; + faveroProduct?: Types.FaveroProduct; + garminProduct?: Types.GarminProduct; + serialNumber?: Types.Uint32z; + /** Only set for files that are can be created/erased. */ + timeCreated?: Types.DateTime; + /** Only set for files that are not created/erased. */ + number?: Types.Uint16; + /** Optional free form string to indicate the devices name or model */ + productName?: Types.String; +} + +export interface FileCreatorMesg extends Mesg { + softwareVersion?: Types.Uint16; + hardwareVersion?: Types.Uint8; +} + +export interface TimestampCorrelationMesg extends Mesg { + /** Whole second part of UTC timestamp at the time the system timestamp was recorded. */ + timestamp?: Types.DateTime; + /** Fractional part of the UTC timestamp at the time the system timestamp was recorded. */ + fractionalTimestamp?: Types.Float64; + /** Whole second part of the system timestamp */ + systemTimestamp?: Types.DateTime; + /** Fractional part of the system timestamp */ + fractionalSystemTimestamp?: Types.Float64; + /** timestamp epoch expressed in local time used to convert timestamps to local time */ + localTimestamp?: Types.LocalDateTime; + /** Millisecond part of the UTC timestamp at the time the system timestamp was recorded. */ + timestampMs?: Types.Uint16; + /** Millisecond part of the system timestamp */ + systemTimestampMs?: Types.Uint16; +} + +export interface SoftwareMesg extends Mesg { + messageIndex?: Types.MessageIndex; + version?: Types.Float64; + partNumber?: Types.String; +} + +export interface SlaveDeviceMesg extends Mesg { + manufacturer?: Types.Manufacturer; + product?: Types.Uint16; + faveroProduct?: Types.FaveroProduct; + garminProduct?: Types.GarminProduct; +} + +export interface CapabilitiesMesg extends Mesg { + /** Use language_bits_x types where x is index of array. */ + languages?: Types.Uint8z[]; + /** Use sport_bits_x types where x is index of array. */ + sports?: Types.SportBits0[]; + workoutsSupported?: Types.WorkoutCapabilities; + connectivitySupported?: Types.ConnectivityCapabilities; +} + +export interface FileCapabilitiesMesg extends Mesg { + messageIndex?: Types.MessageIndex; + type?: Types.File; + flags?: Types.FileFlags; + directory?: Types.String; + maxCount?: Types.Uint16; + maxSize?: Types.Uint32; +} + +export interface MesgCapabilitiesMesg extends Mesg { + messageIndex?: Types.MessageIndex; + file?: Types.File; + mesgNum?: Types.MesgNum; + countType?: Types.MesgCount; + count?: Types.Uint16; + numPerFile?: Types.Uint16; + maxPerFile?: Types.Uint16; + maxPerFileType?: Types.Uint16; +} + +export interface FieldCapabilitiesMesg extends Mesg { + messageIndex?: Types.MessageIndex; + file?: Types.File; + mesgNum?: Types.MesgNum; + fieldNum?: Types.Uint8; + count?: Types.Uint16; +} + +export interface DeviceSettingsMesg extends Mesg { + /** Index into time zone arrays. */ + activeTimeZone?: Types.Uint8; + /** Offset from system time. Required to convert timestamp from system time to UTC. */ + utcOffset?: Types.Uint32; + /** Offset from system time. */ + timeOffset?: Types.Uint32[]; + /** Display mode for the time */ + timeMode?: Types.TimeMode[]; + /** timezone offset in 1/4 hour increments */ + timeZoneOffset?: Types.Float64[]; + /** Mode for backlight */ + backlightMode?: Types.BacklightMode; + /** Enabled state of the activity tracker functionality */ + activityTrackerEnabled?: Types.Bool; + /** UTC timestamp used to set the devices clock and date */ + clockTime?: Types.DateTime; + /** Bitfield to configure enabled screens for each supported loop */ + pagesEnabled?: Types.Uint16[]; + /** Enabled state of the move alert */ + moveAlertEnabled?: Types.Bool; + /** Display mode for the date */ + dateMode?: Types.DateMode; + displayOrientation?: Types.DisplayOrientation; + mountingSide?: Types.Side; + /** Bitfield to indicate one page as default for each supported loop */ + defaultPage?: Types.Uint16[]; + /** Minimum steps before an autosync can occur */ + autosyncMinSteps?: Types.Uint16; + /** Minimum minutes before an autosync can occur */ + autosyncMinTime?: Types.Uint16; + /** Enable auto-detect setting for the lactate threshold feature. */ + lactateThresholdAutodetectEnabled?: Types.Bool; + /** Automatically upload using BLE */ + bleAutoUploadEnabled?: Types.Bool; + /** Helps to conserve battery by changing modes */ + autoSyncFrequency?: Types.AutoSyncFrequency; + /** Allows setting specific activities auto-activity detect enabled/disabled settings */ + autoActivityDetect?: Types.AutoActivityDetect; + /** Number of screens configured to display */ + numberOfScreens?: Types.Uint8; + /** Smart Notification display orientation */ + smartNotificationDisplayOrientation?: Types.DisplayOrientation; + tapInterface?: Types.Switch; + /** Used to hold the tap threshold setting */ + tapSensitivity?: Types.TapSensitivity; +} + +export interface UserProfileMesg extends Mesg { + messageIndex?: Types.MessageIndex; + /** Used for Morning Report greeting */ + friendlyName?: Types.String; + gender?: Types.Gender; + age?: Types.Uint8; + height?: Types.Float64; + weight?: Types.Float64; + language?: Types.Language; + elevSetting?: Types.DisplayMeasure; + weightSetting?: Types.DisplayMeasure; + restingHeartRate?: Types.Uint8; + defaultMaxRunningHeartRate?: Types.Uint8; + defaultMaxBikingHeartRate?: Types.Uint8; + defaultMaxHeartRate?: Types.Uint8; + hrSetting?: Types.DisplayHeart; + speedSetting?: Types.DisplayMeasure; + distSetting?: Types.DisplayMeasure; + powerSetting?: Types.DisplayPower; + activityClass?: Types.ActivityClass; + positionSetting?: Types.DisplayPosition; + temperatureSetting?: Types.DisplayMeasure; + localId?: Types.UserLocalId; + globalId?: Types.Byte[]; + /** Typical wake time */ + wakeTime?: Types.LocaltimeIntoDay; + /** Typical bed time */ + sleepTime?: Types.LocaltimeIntoDay; + heightSetting?: Types.DisplayMeasure; + /** User defined running step length set to 0 for auto length */ + userRunningStepLength?: Types.Float64; + /** User defined walking step length set to 0 for auto length */ + userWalkingStepLength?: Types.Float64; + depthSetting?: Types.DisplayMeasure; + diveCount?: Types.Uint32; +} + +export interface HrmProfileMesg extends Mesg { + messageIndex?: Types.MessageIndex; + enabled?: Types.Bool; + hrmAntId?: Types.Uint16z; + logHrv?: Types.Bool; + hrmAntIdTransType?: Types.Uint8z; +} + +export interface SdmProfileMesg extends Mesg { + messageIndex?: Types.MessageIndex; + enabled?: Types.Bool; + sdmAntId?: Types.Uint16z; + sdmCalFactor?: Types.Float64; + odometer?: Types.Float64; + /** Use footpod for speed source instead of GPS */ + speedSource?: Types.Bool; + sdmAntIdTransType?: Types.Uint8z; + /** Rollover counter that can be used to extend the odometer */ + odometerRollover?: Types.Uint8; +} + +export interface BikeProfileMesg extends Mesg { + messageIndex?: Types.MessageIndex; + name?: Types.String; + sport?: Types.Sport; + subSport?: Types.SubSport; + odometer?: Types.Float64; + bikeSpdAntId?: Types.Uint16z; + bikeCadAntId?: Types.Uint16z; + bikeSpdcadAntId?: Types.Uint16z; + bikePowerAntId?: Types.Uint16z; + customWheelsize?: Types.Float64; + autoWheelsize?: Types.Float64; + bikeWeight?: Types.Float64; + powerCalFactor?: Types.Float64; + autoWheelCal?: Types.Bool; + autoPowerZero?: Types.Bool; + id?: Types.Uint8; + spdEnabled?: Types.Bool; + cadEnabled?: Types.Bool; + spdcadEnabled?: Types.Bool; + powerEnabled?: Types.Bool; + crankLength?: Types.Float64; + enabled?: Types.Bool; + bikeSpdAntIdTransType?: Types.Uint8z; + bikeCadAntIdTransType?: Types.Uint8z; + bikeSpdcadAntIdTransType?: Types.Uint8z; + bikePowerAntIdTransType?: Types.Uint8z; + /** Rollover counter that can be used to extend the odometer */ + odometerRollover?: Types.Uint8; + /** Number of front gears */ + frontGearNum?: Types.Uint8z; + /** Number of teeth on each gear 0 is innermost */ + frontGear?: Types.Uint8z[]; + /** Number of rear gears */ + rearGearNum?: Types.Uint8z; + /** Number of teeth on each gear 0 is innermost */ + rearGear?: Types.Uint8z[]; + shimanoDi2Enabled?: Types.Bool; +} + +export interface ConnectivityMesg extends Mesg { + /** Use Bluetooth for connectivity features */ + bluetoothEnabled?: Types.Bool; + /** Use Bluetooth Low Energy for connectivity features */ + bluetoothLeEnabled?: Types.Bool; + /** Use ANT for connectivity features */ + antEnabled?: Types.Bool; + name?: Types.String; + liveTrackingEnabled?: Types.Bool; + weatherConditionsEnabled?: Types.Bool; + weatherAlertsEnabled?: Types.Bool; + autoActivityUploadEnabled?: Types.Bool; + courseDownloadEnabled?: Types.Bool; + workoutDownloadEnabled?: Types.Bool; + gpsEphemerisDownloadEnabled?: Types.Bool; + incidentDetectionEnabled?: Types.Bool; + grouptrackEnabled?: Types.Bool; +} + +export interface WatchfaceSettingsMesg extends Mesg { + messageIndex?: Types.MessageIndex; + mode?: Types.WatchfaceMode; + layout?: Types.Byte; + digitalLayout?: Types.DigitalWatchfaceLayout; + analogLayout?: Types.AnalogWatchfaceLayout; +} + +export interface OhrSettingsMesg extends Mesg { + timestamp?: Types.DateTime; + enabled?: Types.Switch; +} + +export interface TimeInZoneMesg extends Mesg { + timestamp?: Types.DateTime; + referenceMesg?: Types.MesgNum; + referenceIndex?: Types.MessageIndex; + timeInHrZone?: Types.Float64[]; + timeInSpeedZone?: Types.Float64[]; + timeInCadenceZone?: Types.Float64[]; + timeInPowerZone?: Types.Float64[]; + hrZoneHighBoundary?: Types.Uint8[]; + speedZoneHighBoundary?: Types.Float64[]; + cadenceZoneHighBondary?: Types.Uint8[]; + powerZoneHighBoundary?: Types.Uint16[]; + hrCalcType?: Types.HrZoneCalc; + maxHeartRate?: Types.Uint8; + restingHeartRate?: Types.Uint8; + thresholdHeartRate?: Types.Uint8; + pwrCalcType?: Types.PwrZoneCalc; + functionalThresholdPower?: Types.Uint16; +} + +export interface ZonesTargetMesg extends Mesg { + maxHeartRate?: Types.Uint8; + thresholdHeartRate?: Types.Uint8; + functionalThresholdPower?: Types.Uint16; + hrCalcType?: Types.HrZoneCalc; + pwrCalcType?: Types.PwrZoneCalc; +} + +export interface SportMesg extends Mesg { + sport?: Types.Sport; + subSport?: Types.SubSport; + name?: Types.String; +} + +export interface HrZoneMesg extends Mesg { + messageIndex?: Types.MessageIndex; + highBpm?: Types.Uint8; + name?: Types.String; +} + +export interface SpeedZoneMesg extends Mesg { + messageIndex?: Types.MessageIndex; + highValue?: Types.Float64; + name?: Types.String; +} + +export interface CadenceZoneMesg extends Mesg { + messageIndex?: Types.MessageIndex; + highValue?: Types.Uint8; + name?: Types.String; +} + +export interface PowerZoneMesg extends Mesg { + messageIndex?: Types.MessageIndex; + highValue?: Types.Uint16; + name?: Types.String; +} + +export interface MetZoneMesg extends Mesg { + messageIndex?: Types.MessageIndex; + highBpm?: Types.Uint8; + calories?: Types.Float64; + fatCalories?: Types.Float64; +} + +export interface TrainingSettingsMesg extends Mesg { + targetDistance?: Types.Float64; + targetSpeed?: Types.Float64; + targetTime?: Types.Uint32; + /** A more precise target speed field */ + preciseTargetSpeed?: Types.Float64; +} + +export interface DiveSettingsMesg extends Mesg { + timestamp?: Types.DateTime; + messageIndex?: Types.MessageIndex; + name?: Types.String; + model?: Types.TissueModelType; + gfLow?: Types.Uint8; + gfHigh?: Types.Uint8; + waterType?: Types.WaterType; + /** Fresh water is usually 1000; salt water is usually 1025 */ + waterDensity?: Types.Float32; + /** Typically 1.40 */ + po2Warn?: Types.Float64; + /** Typically 1.60 */ + po2Critical?: Types.Float64; + po2Deco?: Types.Float64; + safetyStopEnabled?: Types.Bool; + bottomDepth?: Types.Float32; + bottomTime?: Types.Uint32; + apneaCountdownEnabled?: Types.Bool; + apneaCountdownTime?: Types.Uint32; + backlightMode?: Types.DiveBacklightMode; + backlightBrightness?: Types.Uint8; + backlightTimeout?: Types.BacklightTimeout; + /** Time between surfacing and ending the activity */ + repeatDiveInterval?: Types.Uint16; + /** Time at safety stop (if enabled) */ + safetyStopTime?: Types.Uint16; + heartRateSourceType?: Types.SourceType; + heartRateSource?: Types.Uint8; + heartRateAntplusDeviceType?: Types.AntplusDeviceType; + heartRateLocalDeviceType?: Types.LocalDeviceType; + /** Index of travel dive_gas message */ + travelGas?: Types.MessageIndex; + /** If low PO2 should be switched to automatically */ + ccrLowSetpointSwitchMode?: Types.CcrSetpointSwitchMode; + /** Target PO2 when using low setpoint */ + ccrLowSetpoint?: Types.Float64; + /** Depth to switch to low setpoint in automatic mode */ + ccrLowSetpointDepth?: Types.Float64; + /** If high PO2 should be switched to automatically */ + ccrHighSetpointSwitchMode?: Types.CcrSetpointSwitchMode; + /** Target PO2 when using high setpoint */ + ccrHighSetpoint?: Types.Float64; + /** Depth to switch to high setpoint in automatic mode */ + ccrHighSetpointDepth?: Types.Float64; + /** Type of gas consumption rate to display. Some values are only valid if tank volume is known. */ + gasConsumptionDisplay?: Types.GasConsumptionRateType; + /** Indicates whether the up key is enabled during dives */ + upKeyEnabled?: Types.Bool; + /** Sounds and vibration enabled or disabled in-dive */ + diveSounds?: Types.Tone; + /** Usually 1.0/1.5/2.0 representing 3/4.5/6m or 10/15/20ft */ + lastStopMultiple?: Types.Float64; + /** Indicates which guidelines to use for no-fly surface interval. */ + noFlyTimeMode?: Types.NoFlyTimeMode; +} + +export interface DiveAlarmMesg extends Mesg { + /** Index of the alarm */ + messageIndex?: Types.MessageIndex; + /** Depth setting (m) for depth type alarms */ + depth?: Types.Float64; + /** Time setting (s) for time type alarms */ + time?: Types.Sint32; + /** Enablement flag */ + enabled?: Types.Bool; + /** Alarm type setting */ + alarmType?: Types.DiveAlarmType; + /** Tone and Vibe setting for the alarm */ + sound?: Types.Tone; + /** Dive types the alarm will trigger on */ + diveTypes?: Types.SubSport[]; + /** Alarm ID */ + id?: Types.Uint32; + /** Show a visible pop-up for this alarm */ + popupEnabled?: Types.Bool; + /** Trigger the alarm on descent */ + triggerOnDescent?: Types.Bool; + /** Trigger the alarm on ascent */ + triggerOnAscent?: Types.Bool; + /** Repeat alarm each time threshold is crossed? */ + repeating?: Types.Bool; + /** Ascent/descent rate (mps) setting for speed type alarms */ + speed?: Types.Float64; +} + +export interface DiveApneaAlarmMesg extends Mesg { + /** Index of the alarm */ + messageIndex?: Types.MessageIndex; + /** Depth setting (m) for depth type alarms */ + depth?: Types.Float64; + /** Time setting (s) for time type alarms */ + time?: Types.Sint32; + /** Enablement flag */ + enabled?: Types.Bool; + /** Alarm type setting */ + alarmType?: Types.DiveAlarmType; + /** Tone and Vibe setting for the alarm. */ + sound?: Types.Tone; + /** Dive types the alarm will trigger on */ + diveTypes?: Types.SubSport[]; + /** Alarm ID */ + id?: Types.Uint32; + /** Show a visible pop-up for this alarm */ + popupEnabled?: Types.Bool; + /** Trigger the alarm on descent */ + triggerOnDescent?: Types.Bool; + /** Trigger the alarm on ascent */ + triggerOnAscent?: Types.Bool; + /** Repeat alarm each time threshold is crossed? */ + repeating?: Types.Bool; + /** Ascent/descent rate (mps) setting for speed type alarms */ + speed?: Types.Float64; +} + +export interface DiveGasMesg extends Mesg { + messageIndex?: Types.MessageIndex; + heliumContent?: Types.Uint8; + oxygenContent?: Types.Uint8; + status?: Types.DiveGasStatus; + mode?: Types.DiveGasMode; +} + +export interface GoalMesg extends Mesg { + messageIndex?: Types.MessageIndex; + sport?: Types.Sport; + subSport?: Types.SubSport; + startDate?: Types.DateTime; + endDate?: Types.DateTime; + type?: Types.Goal; + value?: Types.Uint32; + repeat?: Types.Bool; + targetValue?: Types.Uint32; + recurrence?: Types.GoalRecurrence; + recurrenceValue?: Types.Uint16; + enabled?: Types.Bool; + source?: Types.GoalSource; +} + +export interface ActivityMesg extends Mesg { + timestamp?: Types.DateTime; + /** Exclude pauses */ + totalTimerTime?: Types.Float64; + numSessions?: Types.Uint16; + type?: Types.Activity; + event?: Types.Event; + eventType?: Types.EventType; + /** timestamp epoch expressed in local time, used to convert activity timestamps to local time */ + localTimestamp?: Types.LocalDateTime; + eventGroup?: Types.Uint8; +} + +export interface SessionMesg extends Mesg { + /** Selected bit is set for the current session. */ + messageIndex?: Types.MessageIndex; + timestamp?: Types.DateTime; + /** session */ + event?: Types.Event; + /** stop */ + eventType?: Types.EventType; + startTime?: Types.DateTime; + startPositionLat?: Types.Sint32; + startPositionLong?: Types.Sint32; + sport?: Types.Sport; + subSport?: Types.SubSport; + /** Time (includes pauses) */ + totalElapsedTime?: Types.Float64; + /** Timer Time (excludes pauses) */ + totalTimerTime?: Types.Float64; + totalDistance?: Types.Float64; + totalCycles?: Types.Uint32; + totalStrides?: Types.Uint32; + totalStrokes?: Types.Uint32; + totalCalories?: Types.Uint16; + totalFatCalories?: Types.Uint16; + /** total_distance / total_timer_time */ + avgSpeed?: Types.Float64; + maxSpeed?: Types.Float64; + /** average heart rate (excludes pause time) */ + avgHeartRate?: Types.Uint8; + maxHeartRate?: Types.Uint8; + /** total_cycles / total_timer_time if non_zero_avg_cadence otherwise total_cycles / total_elapsed_time */ + avgCadence?: Types.Uint8; + avgRunningCadence?: Types.Uint8; + maxCadence?: Types.Uint8; + maxRunningCadence?: Types.Uint8; + /** total_power / total_timer_time if non_zero_avg_power otherwise total_power / total_elapsed_time */ + avgPower?: Types.Uint16; + maxPower?: Types.Uint16; + totalAscent?: Types.Uint16; + totalDescent?: Types.Uint16; + totalTrainingEffect?: Types.Float64; + firstLapIndex?: Types.Uint16; + numLaps?: Types.Uint16; + eventGroup?: Types.Uint8; + trigger?: Types.SessionTrigger; + /** North east corner latitude */ + necLat?: Types.Sint32; + /** North east corner longitude */ + necLong?: Types.Sint32; + /** South west corner latitude */ + swcLat?: Types.Sint32; + /** South west corner longitude */ + swcLong?: Types.Sint32; + /** # of lengths of swim pool */ + numLengths?: Types.Uint16; + normalizedPower?: Types.Uint16; + trainingStressScore?: Types.Float64; + intensityFactor?: Types.Float64; + leftRightBalance?: Types.LeftRightBalance100; + endPositionLat?: Types.Sint32; + endPositionLong?: Types.Sint32; + avgStrokeCount?: Types.Float64; + avgStrokeDistance?: Types.Float64; + swimStroke?: Types.SwimStroke; + poolLength?: Types.Float64; + thresholdPower?: Types.Uint16; + poolLengthUnit?: Types.DisplayMeasure; + /** # of active lengths of swim pool */ + numActiveLengths?: Types.Uint16; + totalWork?: Types.Uint32; + avgAltitude?: Types.Float64; + maxAltitude?: Types.Float64; + gpsAccuracy?: Types.Uint8; + avgGrade?: Types.Float64; + avgPosGrade?: Types.Float64; + avgNegGrade?: Types.Float64; + maxPosGrade?: Types.Float64; + maxNegGrade?: Types.Float64; + avgTemperature?: Types.Sint8; + maxTemperature?: Types.Sint8; + totalMovingTime?: Types.Float64; + avgPosVerticalSpeed?: Types.Float64; + avgNegVerticalSpeed?: Types.Float64; + maxPosVerticalSpeed?: Types.Float64; + maxNegVerticalSpeed?: Types.Float64; + minHeartRate?: Types.Uint8; + timeInHrZone?: Types.Float64[]; + timeInSpeedZone?: Types.Float64[]; + timeInCadenceZone?: Types.Float64[]; + timeInPowerZone?: Types.Float64[]; + avgLapTime?: Types.Float64; + bestLapIndex?: Types.Uint16; + minAltitude?: Types.Float64; + activeTime?: Types.Float64; + playerScore?: Types.Uint16; + opponentScore?: Types.Uint16; + opponentName?: Types.String; + /** stroke_type enum used as the index */ + strokeCount?: Types.Uint16[]; + /** zone number used as the index */ + zoneCount?: Types.Uint16[]; + maxBallSpeed?: Types.Float64; + avgBallSpeed?: Types.Float64; + avgVerticalOscillation?: Types.Float64; + avgStanceTimePercent?: Types.Float64; + avgStanceTime?: Types.Float64; + /** fractional part of the avg_cadence */ + avgFractionalCadence?: Types.Float64; + /** fractional part of the max_cadence */ + maxFractionalCadence?: Types.Float64; + /** fractional part of the total_cycles */ + totalFractionalCycles?: Types.Float64; + /** Avg saturated and unsaturated hemoglobin */ + avgTotalHemoglobinConc?: Types.Float64[]; + /** Min saturated and unsaturated hemoglobin */ + minTotalHemoglobinConc?: Types.Float64[]; + /** Max saturated and unsaturated hemoglobin */ + maxTotalHemoglobinConc?: Types.Float64[]; + /** Avg percentage of hemoglobin saturated with oxygen */ + avgSaturatedHemoglobinPercent?: Types.Float64[]; + /** Min percentage of hemoglobin saturated with oxygen */ + minSaturatedHemoglobinPercent?: Types.Float64[]; + /** Max percentage of hemoglobin saturated with oxygen */ + maxSaturatedHemoglobinPercent?: Types.Float64[]; + avgLeftTorqueEffectiveness?: Types.Float64; + avgRightTorqueEffectiveness?: Types.Float64; + avgLeftPedalSmoothness?: Types.Float64; + avgRightPedalSmoothness?: Types.Float64; + avgCombinedPedalSmoothness?: Types.Float64; + /** Sport name from associated sport mesg */ + sportProfileName?: Types.String; + sportIndex?: Types.Uint8; + /** Total time spend in the standing position */ + timeStanding?: Types.Float64; + /** Number of transitions to the standing state */ + standCount?: Types.Uint16; + /** Average platform center offset Left */ + avgLeftPco?: Types.Sint8; + /** Average platform center offset Right */ + avgRightPco?: Types.Sint8; + /** Average left power phase angles. Indexes defined by power_phase_type. */ + avgLeftPowerPhase?: Types.Float64[]; + /** Average left power phase peak angles. Data value indexes defined by power_phase_type. */ + avgLeftPowerPhasePeak?: Types.Float64[]; + /** Average right power phase angles. Data value indexes defined by power_phase_type. */ + avgRightPowerPhase?: Types.Float64[]; + /** Average right power phase peak angles data value indexes defined by power_phase_type. */ + avgRightPowerPhasePeak?: Types.Float64[]; + /** Average power by position. Data value indexes defined by rider_position_type. */ + avgPowerPosition?: Types.Uint16[]; + /** Maximum power by position. Data value indexes defined by rider_position_type. */ + maxPowerPosition?: Types.Uint16[]; + /** Average cadence by position. Data value indexes defined by rider_position_type. */ + avgCadencePosition?: Types.Uint8[]; + /** Maximum cadence by position. Data value indexes defined by rider_position_type. */ + maxCadencePosition?: Types.Uint8[]; + /** total_distance / total_timer_time */ + enhancedAvgSpeed?: Types.Float64; + enhancedMaxSpeed?: Types.Float64; + enhancedAvgAltitude?: Types.Float64; + enhancedMinAltitude?: Types.Float64; + enhancedMaxAltitude?: Types.Float64; + /** lev average motor power during session */ + avgLevMotorPower?: Types.Uint16; + /** lev maximum motor power during session */ + maxLevMotorPower?: Types.Uint16; + /** lev battery consumption during session */ + levBatteryConsumption?: Types.Float64; + avgVerticalRatio?: Types.Float64; + avgStanceTimeBalance?: Types.Float64; + avgStepLength?: Types.Float64; + totalAnaerobicTrainingEffect?: Types.Float64; + avgVam?: Types.Float64; + /** 0 if above water */ + avgDepth?: Types.Float64; + /** 0 if above water */ + maxDepth?: Types.Float64; + /** Time since end of last dive */ + surfaceInterval?: Types.Uint32; + startCns?: Types.Uint8; + endCns?: Types.Uint8; + startN2?: Types.Uint16; + endN2?: Types.Uint16; + avgRespirationRate?: Types.Uint8; + maxRespirationRate?: Types.Uint8; + minRespirationRate?: Types.Uint8; + minTemperature?: Types.Sint8; + o2Toxicity?: Types.Uint16; + diveNumber?: Types.Uint32; + trainingLoadPeak?: Types.Float64; + enhancedAvgRespirationRate?: Types.Float64; + enhancedMaxRespirationRate?: Types.Float64; + enhancedMinRespirationRate?: Types.Float64; + /** The grit score estimates how challenging a route could be for a cyclist in terms of time spent going over sharp turns or large grade slopes. */ + totalGrit?: Types.Float32; + /** The flow score estimates how long distance wise a cyclist deaccelerates over intervals where deacceleration is unnecessary such as smooth turns or small grade angle intervals. */ + totalFlow?: Types.Float32; + jumpCount?: Types.Uint16; + /** The grit score estimates how challenging a route could be for a cyclist in terms of time spent going over sharp turns or large grade slopes. */ + avgGrit?: Types.Float32; + /** The flow score estimates how long distance wise a cyclist deaccelerates over intervals where deacceleration is unnecessary such as smooth turns or small grade angle intervals. */ + avgFlow?: Types.Float32; + /** A 0-100 scale representing how a user felt while performing a workout. Low values are considered feeling bad, while high values are good. */ + workoutFeel?: Types.Uint8; + /** Common Borg CR10 / 0-10 RPE scale, multiplied 10x.. Aggregate score for all workouts in a single session. */ + workoutRpe?: Types.Uint8; + /** Average SPO2 for the monitoring session */ + avgSpo2?: Types.Uint8; + /** Average stress for the monitoring session */ + avgStress?: Types.Uint8; + metabolicCalories?: Types.Uint16; + /** Standard deviation of R-R interval (SDRR) - Heart rate variability measure most useful for wellness users. */ + sdrrHrv?: Types.Uint8; + /** Root mean square successive difference (RMSSD) - Heart rate variability measure most useful for athletes */ + rmssdHrv?: Types.Uint8; + /** fractional part of total_ascent */ + totalFractionalAscent?: Types.Float64; + /** fractional part of total_descent */ + totalFractionalDescent?: Types.Float64; + avgCoreTemperature?: Types.Float64; + minCoreTemperature?: Types.Float64; + maxCoreTemperature?: Types.Float64; +} + +export interface LapMesg extends Mesg { + messageIndex?: Types.MessageIndex; + timestamp?: Types.DateTime; + event?: Types.Event; + eventType?: Types.EventType; + startTime?: Types.DateTime; + startPositionLat?: Types.Sint32; + startPositionLong?: Types.Sint32; + endPositionLat?: Types.Sint32; + endPositionLong?: Types.Sint32; + /** Time (includes pauses) */ + totalElapsedTime?: Types.Float64; + /** Timer Time (excludes pauses) */ + totalTimerTime?: Types.Float64; + totalDistance?: Types.Float64; + totalCycles?: Types.Uint32; + totalStrides?: Types.Uint32; + totalStrokes?: Types.Uint32; + totalCalories?: Types.Uint16; + /** If New Leaf */ + totalFatCalories?: Types.Uint16; + avgSpeed?: Types.Float64; + maxSpeed?: Types.Float64; + avgHeartRate?: Types.Uint8; + maxHeartRate?: Types.Uint8; + /** total_cycles / total_timer_time if non_zero_avg_cadence otherwise total_cycles / total_elapsed_time */ + avgCadence?: Types.Uint8; + avgRunningCadence?: Types.Uint8; + maxCadence?: Types.Uint8; + maxRunningCadence?: Types.Uint8; + /** total_power / total_timer_time if non_zero_avg_power otherwise total_power / total_elapsed_time */ + avgPower?: Types.Uint16; + maxPower?: Types.Uint16; + totalAscent?: Types.Uint16; + totalDescent?: Types.Uint16; + intensity?: Types.Intensity; + lapTrigger?: Types.LapTrigger; + sport?: Types.Sport; + eventGroup?: Types.Uint8; + /** # of lengths of swim pool */ + numLengths?: Types.Uint16; + normalizedPower?: Types.Uint16; + leftRightBalance?: Types.LeftRightBalance100; + firstLengthIndex?: Types.Uint16; + avgStrokeDistance?: Types.Float64; + swimStroke?: Types.SwimStroke; + subSport?: Types.SubSport; + /** # of active lengths of swim pool */ + numActiveLengths?: Types.Uint16; + totalWork?: Types.Uint32; + avgAltitude?: Types.Float64; + maxAltitude?: Types.Float64; + gpsAccuracy?: Types.Uint8; + avgGrade?: Types.Float64; + avgPosGrade?: Types.Float64; + avgNegGrade?: Types.Float64; + maxPosGrade?: Types.Float64; + maxNegGrade?: Types.Float64; + avgTemperature?: Types.Sint8; + maxTemperature?: Types.Sint8; + totalMovingTime?: Types.Float64; + avgPosVerticalSpeed?: Types.Float64; + avgNegVerticalSpeed?: Types.Float64; + maxPosVerticalSpeed?: Types.Float64; + maxNegVerticalSpeed?: Types.Float64; + timeInHrZone?: Types.Float64[]; + timeInSpeedZone?: Types.Float64[]; + timeInCadenceZone?: Types.Float64[]; + timeInPowerZone?: Types.Float64[]; + repetitionNum?: Types.Uint16; + minAltitude?: Types.Float64; + minHeartRate?: Types.Uint8; + activeTime?: Types.Float64; + wktStepIndex?: Types.MessageIndex; + opponentScore?: Types.Uint16; + /** stroke_type enum used as the index */ + strokeCount?: Types.Uint16[]; + /** zone number used as the index */ + zoneCount?: Types.Uint16[]; + avgVerticalOscillation?: Types.Float64; + avgStanceTimePercent?: Types.Float64; + avgStanceTime?: Types.Float64; + /** fractional part of the avg_cadence */ + avgFractionalCadence?: Types.Float64; + /** fractional part of the max_cadence */ + maxFractionalCadence?: Types.Float64; + /** fractional part of the total_cycles */ + totalFractionalCycles?: Types.Float64; + playerScore?: Types.Uint16; + /** Avg saturated and unsaturated hemoglobin */ + avgTotalHemoglobinConc?: Types.Float64[]; + /** Min saturated and unsaturated hemoglobin */ + minTotalHemoglobinConc?: Types.Float64[]; + /** Max saturated and unsaturated hemoglobin */ + maxTotalHemoglobinConc?: Types.Float64[]; + /** Avg percentage of hemoglobin saturated with oxygen */ + avgSaturatedHemoglobinPercent?: Types.Float64[]; + /** Min percentage of hemoglobin saturated with oxygen */ + minSaturatedHemoglobinPercent?: Types.Float64[]; + /** Max percentage of hemoglobin saturated with oxygen */ + maxSaturatedHemoglobinPercent?: Types.Float64[]; + avgLeftTorqueEffectiveness?: Types.Float64; + avgRightTorqueEffectiveness?: Types.Float64; + avgLeftPedalSmoothness?: Types.Float64; + avgRightPedalSmoothness?: Types.Float64; + avgCombinedPedalSmoothness?: Types.Float64; + /** Total time spent in the standing position */ + timeStanding?: Types.Float64; + /** Number of transitions to the standing state */ + standCount?: Types.Uint16; + /** Average left platform center offset */ + avgLeftPco?: Types.Sint8; + /** Average right platform center offset */ + avgRightPco?: Types.Sint8; + /** Average left power phase angles. Data value indexes defined by power_phase_type. */ + avgLeftPowerPhase?: Types.Float64[]; + /** Average left power phase peak angles. Data value indexes defined by power_phase_type. */ + avgLeftPowerPhasePeak?: Types.Float64[]; + /** Average right power phase angles. Data value indexes defined by power_phase_type. */ + avgRightPowerPhase?: Types.Float64[]; + /** Average right power phase peak angles. Data value indexes defined by power_phase_type. */ + avgRightPowerPhasePeak?: Types.Float64[]; + /** Average power by position. Data value indexes defined by rider_position_type. */ + avgPowerPosition?: Types.Uint16[]; + /** Maximum power by position. Data value indexes defined by rider_position_type. */ + maxPowerPosition?: Types.Uint16[]; + /** Average cadence by position. Data value indexes defined by rider_position_type. */ + avgCadencePosition?: Types.Uint8[]; + /** Maximum cadence by position. Data value indexes defined by rider_position_type. */ + maxCadencePosition?: Types.Uint8[]; + enhancedAvgSpeed?: Types.Float64; + enhancedMaxSpeed?: Types.Float64; + enhancedAvgAltitude?: Types.Float64; + enhancedMinAltitude?: Types.Float64; + enhancedMaxAltitude?: Types.Float64; + /** lev average motor power during lap */ + avgLevMotorPower?: Types.Uint16; + /** lev maximum motor power during lap */ + maxLevMotorPower?: Types.Uint16; + /** lev battery consumption during lap */ + levBatteryConsumption?: Types.Float64; + avgVerticalRatio?: Types.Float64; + avgStanceTimeBalance?: Types.Float64; + avgStepLength?: Types.Float64; + avgVam?: Types.Float64; + /** 0 if above water */ + avgDepth?: Types.Float64; + /** 0 if above water */ + maxDepth?: Types.Float64; + minTemperature?: Types.Sint8; + enhancedAvgRespirationRate?: Types.Float64; + enhancedMaxRespirationRate?: Types.Float64; + avgRespirationRate?: Types.Uint8; + maxRespirationRate?: Types.Uint8; + /** The grit score estimates how challenging a route could be for a cyclist in terms of time spent going over sharp turns or large grade slopes. */ + totalGrit?: Types.Float32; + /** The flow score estimates how long distance wise a cyclist deaccelerates over intervals where deacceleration is unnecessary such as smooth turns or small grade angle intervals. */ + totalFlow?: Types.Float32; + jumpCount?: Types.Uint16; + /** The grit score estimates how challenging a route could be for a cyclist in terms of time spent going over sharp turns or large grade slopes. */ + avgGrit?: Types.Float32; + /** The flow score estimates how long distance wise a cyclist deaccelerates over intervals where deacceleration is unnecessary such as smooth turns or small grade angle intervals. */ + avgFlow?: Types.Float32; + /** fractional part of total_ascent */ + totalFractionalAscent?: Types.Float64; + /** fractional part of total_descent */ + totalFractionalDescent?: Types.Float64; + avgCoreTemperature?: Types.Float64; + minCoreTemperature?: Types.Float64; + maxCoreTemperature?: Types.Float64; +} + +export interface LengthMesg extends Mesg { + messageIndex?: Types.MessageIndex; + timestamp?: Types.DateTime; + event?: Types.Event; + eventType?: Types.EventType; + startTime?: Types.DateTime; + totalElapsedTime?: Types.Float64; + totalTimerTime?: Types.Float64; + totalStrokes?: Types.Uint16; + avgSpeed?: Types.Float64; + swimStroke?: Types.SwimStroke; + avgSwimmingCadence?: Types.Uint8; + eventGroup?: Types.Uint8; + totalCalories?: Types.Uint16; + lengthType?: Types.LengthType; + playerScore?: Types.Uint16; + opponentScore?: Types.Uint16; + /** stroke_type enum used as the index */ + strokeCount?: Types.Uint16[]; + /** zone number used as the index */ + zoneCount?: Types.Uint16[]; + enhancedAvgRespirationRate?: Types.Float64; + enhancedMaxRespirationRate?: Types.Float64; + avgRespirationRate?: Types.Uint8; + maxRespirationRate?: Types.Uint8; +} + +export interface RecordMesg extends Mesg { + timestamp?: Types.DateTime; + positionLat?: Types.Sint32; + positionLong?: Types.Sint32; + altitude?: Types.Float64; + heartRate?: Types.Uint8; + cadence?: Types.Uint8; + distance?: Types.Float64; + speed?: Types.Float64; + power?: Types.Uint16; + compressedSpeedDistance?: Types.Byte[]; + grade?: Types.Float64; + /** Relative. 0 is none 254 is Max. */ + resistance?: Types.Uint8; + timeFromCourse?: Types.Float64; + cycleLength?: Types.Float64; + temperature?: Types.Sint8; + /** Speed at 1s intervals. Timestamp field indicates time of last array element. */ + speed1s?: Types.Float64[]; + cycles?: Types.Uint8; + totalCycles?: Types.Uint32; + compressedAccumulatedPower?: Types.Uint16; + accumulatedPower?: Types.Uint32; + leftRightBalance?: Types.LeftRightBalance; + gpsAccuracy?: Types.Uint8; + verticalSpeed?: Types.Float64; + calories?: Types.Uint16; + verticalOscillation?: Types.Float64; + stanceTimePercent?: Types.Float64; + stanceTime?: Types.Float64; + activityType?: Types.ActivityType; + leftTorqueEffectiveness?: Types.Float64; + rightTorqueEffectiveness?: Types.Float64; + leftPedalSmoothness?: Types.Float64; + rightPedalSmoothness?: Types.Float64; + combinedPedalSmoothness?: Types.Float64; + time128?: Types.Float64; + strokeType?: Types.StrokeType; + zone?: Types.Uint8; + ballSpeed?: Types.Float64; + /** Log cadence and fractional cadence for backwards compatability */ + cadence256?: Types.Float64; + fractionalCadence?: Types.Float64; + /** Total saturated and unsaturated hemoglobin */ + totalHemoglobinConc?: Types.Float64; + /** Min saturated and unsaturated hemoglobin */ + totalHemoglobinConcMin?: Types.Float64; + /** Max saturated and unsaturated hemoglobin */ + totalHemoglobinConcMax?: Types.Float64; + /** Percentage of hemoglobin saturated with oxygen */ + saturatedHemoglobinPercent?: Types.Float64; + /** Min percentage of hemoglobin saturated with oxygen */ + saturatedHemoglobinPercentMin?: Types.Float64; + /** Max percentage of hemoglobin saturated with oxygen */ + saturatedHemoglobinPercentMax?: Types.Float64; + deviceIndex?: Types.DeviceIndex; + /** Left platform center offset */ + leftPco?: Types.Sint8; + /** Right platform center offset */ + rightPco?: Types.Sint8; + /** Left power phase angles. Data value indexes defined by power_phase_type. */ + leftPowerPhase?: Types.Float64[]; + /** Left power phase peak angles. Data value indexes defined by power_phase_type. */ + leftPowerPhasePeak?: Types.Float64[]; + /** Right power phase angles. Data value indexes defined by power_phase_type. */ + rightPowerPhase?: Types.Float64[]; + /** Right power phase peak angles. Data value indexes defined by power_phase_type. */ + rightPowerPhasePeak?: Types.Float64[]; + enhancedSpeed?: Types.Float64; + enhancedAltitude?: Types.Float64; + /** lev battery state of charge */ + batterySoc?: Types.Float64; + /** lev motor power */ + motorPower?: Types.Uint16; + verticalRatio?: Types.Float64; + stanceTimeBalance?: Types.Float64; + stepLength?: Types.Float64; + /** Supports larger cycle sizes needed for paddlesports. Max cycle size: 655.35 */ + cycleLength16?: Types.Float64; + /** Includes atmospheric pressure */ + absolutePressure?: Types.Uint32; + /** 0 if above water */ + depth?: Types.Float64; + /** 0 if above water */ + nextStopDepth?: Types.Float64; + nextStopTime?: Types.Uint32; + timeToSurface?: Types.Uint32; + ndlTime?: Types.Uint32; + cnsLoad?: Types.Uint8; + n2Load?: Types.Uint16; + respirationRate?: Types.Uint8; + enhancedRespirationRate?: Types.Float64; + /** The grit score estimates how challenging a route could be for a cyclist in terms of time spent going over sharp turns or large grade slopes. */ + grit?: Types.Float32; + /** The flow score estimates how long distance wise a cyclist deaccelerates over intervals where deacceleration is unnecessary such as smooth turns or small grade angle intervals. */ + flow?: Types.Float32; + /** Current Stress value */ + currentStress?: Types.Float64; + ebikeTravelRange?: Types.Uint16; + ebikeBatteryLevel?: Types.Uint8; + ebikeAssistMode?: Types.Uint8; + ebikeAssistLevelPercent?: Types.Uint8; + airTimeRemaining?: Types.Uint32; + /** Pressure-based surface air consumption */ + pressureSac?: Types.Float64; + /** Volumetric surface air consumption */ + volumeSac?: Types.Float64; + /** Respiratory minute volume */ + rmv?: Types.Float64; + ascentRate?: Types.Float64; + /** Current partial pressure of oxygen */ + po2?: Types.Float64; + coreTemperature?: Types.Float64; +} + +export interface EventMesg extends Mesg { + timestamp?: Types.DateTime; + event?: Types.Event; + eventType?: Types.EventType; + data16?: Types.Uint16; + data?: Types.Uint32; + timerTrigger?: Types.TimerTrigger; + coursePointIndex?: Types.MessageIndex; + batteryLevel?: Types.Float64; + virtualPartnerSpeed?: Types.Float64; + hrHighAlert?: Types.Uint8; + hrLowAlert?: Types.Uint8; + speedHighAlert?: Types.Float64; + speedLowAlert?: Types.Float64; + cadHighAlert?: Types.Uint16; + cadLowAlert?: Types.Uint16; + powerHighAlert?: Types.Uint16; + powerLowAlert?: Types.Uint16; + timeDurationAlert?: Types.Float64; + distanceDurationAlert?: Types.Float64; + calorieDurationAlert?: Types.Uint32; + fitnessEquipmentState?: Types.FitnessEquipmentState; + sportPoint?: Types.Uint32; + gearChangeData?: Types.Uint32; + /** Indicates the rider position value.*/ + riderPosition?: Types.RiderPositionType; + commTimeout?: Types.CommTimeoutType; + diveAlert?: Types.DiveAlert; + autoActivityDetectDuration?: Types.Uint16; + /** The first byte is the radar_threat_level_max, the second byte is the radar_threat_count, third bytes is the average approach speed, and the 4th byte is the max approach speed*/ + radarThreatAlert?: Types.Uint32; + eventGroup?: Types.Uint8; + /** Do not populate directly. Autogenerated by decoder for sport_point subfield components */ + score?: Types.Uint16; + /** Do not populate directly. Autogenerated by decoder for sport_point subfield components */ + opponentScore?: Types.Uint16; + /** Do not populate directly. Autogenerated by decoder for gear_change subfield components. Front gear number. 1 is innermost. */ + frontGearNum?: Types.Uint8z; + /** Do not populate directly. Autogenerated by decoder for gear_change subfield components. Number of front teeth. */ + frontGear?: Types.Uint8z; + /** Do not populate directly. Autogenerated by decoder for gear_change subfield components. Rear gear number. 1 is innermost. */ + rearGearNum?: Types.Uint8z; + /** Do not populate directly. Autogenerated by decoder for gear_change subfield components. Number of rear teeth. */ + rearGear?: Types.Uint8z; + deviceIndex?: Types.DeviceIndex; + /** Activity Type associated with an auto_activity_detect event */ + activityType?: Types.ActivityType; + /** Timestamp of when the event started */ + startTimestamp?: Types.DateTime; + /** Auto Activity Detect Start Timestamp.*/ + autoActivityDetectStartTimestamp?: Types.DateTime; + /** Do not populate directly. Autogenerated by decoder for threat_alert subfield components. */ + radarThreatLevelMax?: Types.RadarThreatLevelType; + /** Do not populate directly. Autogenerated by decoder for threat_alert subfield components. */ + radarThreatCount?: Types.Uint8; + /** Do not populate directly. Autogenerated by decoder for radar_threat_alert subfield components */ + radarThreatAvgApproachSpeed?: Types.Float64; + /** Do not populate directly. Autogenerated by decoder for radar_threat_alert subfield components */ + radarThreatMaxApproachSpeed?: Types.Float64; +} + +export interface DeviceInfoMesg extends Mesg { + timestamp?: Types.DateTime; + deviceIndex?: Types.DeviceIndex; + deviceType?: Types.Uint8; + bleDeviceType?: Types.BleDeviceType; + antplusDeviceType?: Types.AntplusDeviceType; + antDeviceType?: Types.Uint8; + localDeviceType?: Types.LocalDeviceType; + manufacturer?: Types.Manufacturer; + serialNumber?: Types.Uint32z; + product?: Types.Uint16; + faveroProduct?: Types.FaveroProduct; + garminProduct?: Types.GarminProduct; + softwareVersion?: Types.Float64; + hardwareVersion?: Types.Uint8; + /** Reset by new battery or charge. */ + cumOperatingTime?: Types.Uint32; + batteryVoltage?: Types.Float64; + batteryStatus?: Types.BatteryStatus; + /** Indicates the location of the sensor */ + sensorPosition?: Types.BodyLocation; + /** Used to describe the sensor or location */ + descriptor?: Types.String; + antTransmissionType?: Types.Uint8z; + antDeviceNumber?: Types.Uint16z; + antNetwork?: Types.AntNetwork; + sourceType?: Types.SourceType; + /** Optional free form string to indicate the devices name or model */ + productName?: Types.String; + batteryLevel?: Types.Uint8; +} + +export interface DeviceAuxBatteryInfoMesg extends Mesg { + timestamp?: Types.DateTime; + deviceIndex?: Types.DeviceIndex; + batteryVoltage?: Types.Float64; + batteryStatus?: Types.BatteryStatus; + batteryIdentifier?: Types.Uint8; +} + +/** + * Corresponds to file_id of workout or course. + */ +export interface TrainingFileMesg extends Mesg { + timestamp?: Types.DateTime; + type?: Types.File; + manufacturer?: Types.Manufacturer; + product?: Types.Uint16; + faveroProduct?: Types.FaveroProduct; + garminProduct?: Types.GarminProduct; + serialNumber?: Types.Uint32z; + timeCreated?: Types.DateTime; +} + +export interface WeatherConditionsMesg extends Mesg { + /** time of update for current conditions, else forecast time */ + timestamp?: Types.DateTime; + /** Current or forecast */ + weatherReport?: Types.WeatherReport; + temperature?: Types.Sint8; + /** Corresponds to GSC Response weatherIcon field */ + condition?: Types.WeatherStatus; + windDirection?: Types.Uint16; + windSpeed?: Types.Float64; + /** range 0-100 */ + precipitationProbability?: Types.Uint8; + /** Heat Index if GCS heatIdx above or equal to 90F or wind chill if GCS windChill below or equal to 32F */ + temperatureFeelsLike?: Types.Sint8; + relativeHumidity?: Types.Uint8; + /** string corresponding to GCS response location string */ + location?: Types.String; + observedAtTime?: Types.DateTime; + observedLocationLat?: Types.Sint32; + observedLocationLong?: Types.Sint32; + dayOfWeek?: Types.DayOfWeek; + highTemperature?: Types.Sint8; + lowTemperature?: Types.Sint8; +} + +export interface WeatherAlertMesg extends Mesg { + timestamp?: Types.DateTime; + /** Unique identifier from GCS report ID string, length is 12 */ + reportId?: Types.String; + /** Time alert was issued */ + issueTime?: Types.DateTime; + /** Time alert expires */ + expireTime?: Types.DateTime; + /** Warning, Watch, Advisory, Statement */ + severity?: Types.WeatherSeverity; + /** Tornado, Severe Thunderstorm, etc. */ + type?: Types.WeatherSevereType; +} + +export interface GpsMetadataMesg extends Mesg { + /** Whole second part of the timestamp. */ + timestamp?: Types.DateTime; + /** Millisecond part of the timestamp. */ + timestampMs?: Types.Uint16; + positionLat?: Types.Sint32; + positionLong?: Types.Sint32; + enhancedAltitude?: Types.Float64; + enhancedSpeed?: Types.Float64; + heading?: Types.Float64; + /** Used to correlate UTC to system time if the timestamp of the message is in system time. This UTC time is derived from the GPS data. */ + utcTimestamp?: Types.DateTime; + /** velocity[0] is lon velocity. Velocity[1] is lat velocity. Velocity[2] is altitude velocity. */ + velocity?: Types.Float64[]; +} + +export interface CameraEventMesg extends Mesg { + /** Whole second part of the timestamp. */ + timestamp?: Types.DateTime; + /** Millisecond part of the timestamp. */ + timestampMs?: Types.Uint16; + cameraEventType?: Types.CameraEventType; + cameraFileUuid?: Types.String; + cameraOrientation?: Types.CameraOrientationType; +} + +export interface GyroscopeDataMesg extends Mesg { + /** Whole second part of the timestamp */ + timestamp?: Types.DateTime; + /** Millisecond part of the timestamp. */ + timestampMs?: Types.Uint16; + /** Each time in the array describes the time at which the gyro sample with the corrosponding index was taken. Limited to 30 samples in each message. The samples may span across seconds. Array size must match the number of samples in gyro_x and gyro_y and gyro_z */ + sampleTimeOffset?: Types.Uint16[]; + /** These are the raw ADC reading. Maximum number of samples is 30 in each message. The samples may span across seconds. A conversion will need to be done on this data once read. */ + gyroX?: Types.Uint16[]; + /** These are the raw ADC reading. Maximum number of samples is 30 in each message. The samples may span across seconds. A conversion will need to be done on this data once read. */ + gyroY?: Types.Uint16[]; + /** These are the raw ADC reading. Maximum number of samples is 30 in each message. The samples may span across seconds. A conversion will need to be done on this data once read. */ + gyroZ?: Types.Uint16[]; + /** Calibrated gyro reading */ + calibratedGyroX?: Types.Float32[]; + /** Calibrated gyro reading */ + calibratedGyroY?: Types.Float32[]; + /** Calibrated gyro reading */ + calibratedGyroZ?: Types.Float32[]; +} + +export interface AccelerometerDataMesg extends Mesg { + /** Whole second part of the timestamp */ + timestamp?: Types.DateTime; + /** Millisecond part of the timestamp. */ + timestampMs?: Types.Uint16; + /** Each time in the array describes the time at which the accelerometer sample with the corrosponding index was taken. Limited to 30 samples in each message. The samples may span across seconds. Array size must match the number of samples in accel_x and accel_y and accel_z */ + sampleTimeOffset?: Types.Uint16[]; + /** These are the raw ADC reading. Maximum number of samples is 30 in each message. The samples may span across seconds. A conversion will need to be done on this data once read. */ + accelX?: Types.Uint16[]; + /** These are the raw ADC reading. Maximum number of samples is 30 in each message. The samples may span across seconds. A conversion will need to be done on this data once read. */ + accelY?: Types.Uint16[]; + /** These are the raw ADC reading. Maximum number of samples is 30 in each message. The samples may span across seconds. A conversion will need to be done on this data once read. */ + accelZ?: Types.Uint16[]; + /** Calibrated accel reading */ + calibratedAccelX?: Types.Float32[]; + /** Calibrated accel reading */ + calibratedAccelY?: Types.Float32[]; + /** Calibrated accel reading */ + calibratedAccelZ?: Types.Float32[]; + /** Calibrated accel reading */ + compressedCalibratedAccelX?: Types.Sint16[]; + /** Calibrated accel reading */ + compressedCalibratedAccelY?: Types.Sint16[]; + /** Calibrated accel reading */ + compressedCalibratedAccelZ?: Types.Sint16[]; +} + +export interface MagnetometerDataMesg extends Mesg { + /** Whole second part of the timestamp */ + timestamp?: Types.DateTime; + /** Millisecond part of the timestamp. */ + timestampMs?: Types.Uint16; + /** Each time in the array describes the time at which the compass sample with the corrosponding index was taken. Limited to 30 samples in each message. The samples may span across seconds. Array size must match the number of samples in cmps_x and cmps_y and cmps_z */ + sampleTimeOffset?: Types.Uint16[]; + /** These are the raw ADC reading. Maximum number of samples is 30 in each message. The samples may span across seconds. A conversion will need to be done on this data once read. */ + magX?: Types.Uint16[]; + /** These are the raw ADC reading. Maximum number of samples is 30 in each message. The samples may span across seconds. A conversion will need to be done on this data once read. */ + magY?: Types.Uint16[]; + /** These are the raw ADC reading. Maximum number of samples is 30 in each message. The samples may span across seconds. A conversion will need to be done on this data once read. */ + magZ?: Types.Uint16[]; + /** Calibrated Magnetometer reading */ + calibratedMagX?: Types.Float32[]; + /** Calibrated Magnetometer reading */ + calibratedMagY?: Types.Float32[]; + /** Calibrated Magnetometer reading */ + calibratedMagZ?: Types.Float32[]; +} + +export interface BarometerDataMesg extends Mesg { + /** Whole second part of the timestamp */ + timestamp?: Types.DateTime; + /** Millisecond part of the timestamp. */ + timestampMs?: Types.Uint16; + /** Each time in the array describes the time at which the barometer sample with the corrosponding index was taken. The samples may span across seconds. Array size must match the number of samples in baro_cal */ + sampleTimeOffset?: Types.Uint16[]; + /** These are the raw ADC reading. The samples may span across seconds. A conversion will need to be done on this data once read. */ + baroPres?: Types.Uint32[]; +} + +export interface ThreeDSensorCalibrationMesg extends Mesg { + /** Whole second part of the timestamp */ + timestamp?: Types.DateTime; + /** Indicates which sensor the calibration is for */ + sensorType?: Types.SensorType; + /** Calibration factor used to convert from raw ADC value to degrees, g, etc. */ + calibrationFactor?: Types.Uint32; + /** Accelerometer calibration factor*/ + accelCalFactor?: Types.Uint32; + /** Gyro calibration factor*/ + gyroCalFactor?: Types.Uint32; + /** Calibration factor divisor */ + calibrationDivisor?: Types.Uint32; + /** Level shift value used to shift the ADC value back into range */ + levelShift?: Types.Uint32; + /** Internal calibration factors, one for each: xy, yx, zx */ + offsetCal?: Types.Sint32[]; + /** 3 x 3 rotation matrix (row major) */ + orientationMatrix?: Types.Float64[]; +} + +export interface OneDSensorCalibrationMesg extends Mesg { + /** Whole second part of the timestamp */ + timestamp?: Types.DateTime; + /** Indicates which sensor the calibration is for */ + sensorType?: Types.SensorType; + /** Calibration factor used to convert from raw ADC value to degrees, g, etc. */ + calibrationFactor?: Types.Uint32; + /** Barometer calibration factor*/ + baroCalFactor?: Types.Uint32; + /** Calibration factor divisor */ + calibrationDivisor?: Types.Uint32; + /** Level shift value used to shift the ADC value back into range */ + levelShift?: Types.Uint32; + /** Internal Calibration factor */ + offsetCal?: Types.Sint32; +} + +export interface VideoFrameMesg extends Mesg { + /** Whole second part of the timestamp */ + timestamp?: Types.DateTime; + /** Millisecond part of the timestamp. */ + timestampMs?: Types.Uint16; + /** Number of the frame that the timestamp and timestamp_ms correlate to */ + frameNumber?: Types.Uint32; +} + +export interface ObdiiDataMesg extends Mesg { + /** Timestamp message was output */ + timestamp?: Types.DateTime; + /** Fractional part of timestamp, added to timestamp */ + timestampMs?: Types.Uint16; + /** Offset of PID reading [i] from start_timestamp+start_timestamp_ms. Readings may span accross seconds. */ + timeOffset?: Types.Uint16[]; + /** Parameter ID */ + pid?: Types.Byte; + /** Raw parameter data */ + rawData?: Types.Byte[]; + /** Optional, data size of PID[i]. If not specified refer to SAE J1979. */ + pidDataSize?: Types.Uint8[]; + /** System time associated with sample expressed in ms, can be used instead of time_offset. There will be a system_time value for each raw_data element. For multibyte pids the system_time is repeated. */ + systemTime?: Types.Uint32[]; + /** Timestamp of first sample recorded in the message. Used with time_offset to generate time of each sample */ + startTimestamp?: Types.DateTime; + /** Fractional part of start_timestamp */ + startTimestampMs?: Types.Uint16; +} + +export interface NmeaSentenceMesg extends Mesg { + /** Timestamp message was output */ + timestamp?: Types.DateTime; + /** Fractional part of timestamp, added to timestamp */ + timestampMs?: Types.Uint16; + /** NMEA sentence */ + sentence?: Types.String; +} + +export interface AviationAttitudeMesg extends Mesg { + /** Timestamp message was output */ + timestamp?: Types.DateTime; + /** Fractional part of timestamp, added to timestamp */ + timestampMs?: Types.Uint16; + /** System time associated with sample expressed in ms. */ + systemTime?: Types.Uint32[]; + /** Range -PI/2 to +PI/2 */ + pitch?: Types.Float64[]; + /** Range -PI to +PI */ + roll?: Types.Float64[]; + /** Range -78.4 to +78.4 (-8 Gs to 8 Gs) */ + accelLateral?: Types.Float64[]; + /** Range -78.4 to +78.4 (-8 Gs to 8 Gs) */ + accelNormal?: Types.Float64[]; + /** Range -8.727 to +8.727 (-500 degs/sec to +500 degs/sec) */ + turnRate?: Types.Float64[]; + stage?: Types.AttitudeStage[]; + /** The percent complete of the current attitude stage. Set to 0 for attitude stages 0, 1 and 2 and to 100 for attitude stage 3 by AHRS modules that do not support it. Range - 100 */ + attitudeStageComplete?: Types.Uint8[]; + /** Track Angle/Heading Range 0 - 2pi */ + track?: Types.Float64[]; + validity?: Types.AttitudeValidity[]; +} + +export interface VideoMesg extends Mesg { + url?: Types.String; + hostingProvider?: Types.String; + /** Playback time of video */ + duration?: Types.Uint32; +} + +export interface VideoTitleMesg extends Mesg { + /** Long titles will be split into multiple parts */ + messageIndex?: Types.MessageIndex; + /** Total number of title parts */ + messageCount?: Types.Uint16; + text?: Types.String; +} + +export interface VideoDescriptionMesg extends Mesg { + /** Long descriptions will be split into multiple parts */ + messageIndex?: Types.MessageIndex; + /** Total number of description parts */ + messageCount?: Types.Uint16; + text?: Types.String; +} + +export interface VideoClipMesg extends Mesg { + clipNumber?: Types.Uint16; + startTimestamp?: Types.DateTime; + startTimestampMs?: Types.Uint16; + endTimestamp?: Types.DateTime; + endTimestampMs?: Types.Uint16; + /** Start of clip in video time */ + clipStart?: Types.Uint32; + /** End of clip in video time */ + clipEnd?: Types.Uint32; +} + +export interface SetMesg extends Mesg { + /** Timestamp of the set */ + timestamp?: Types.DateTime; + duration?: Types.Float64; + /** # of repitions of the movement */ + repetitions?: Types.Uint16; + /** Amount of weight applied for the set */ + weight?: Types.Float64; + setType?: Types.SetType; + /** Start time of the set */ + startTime?: Types.DateTime; + category?: Types.ExerciseCategory[]; + /** Based on the associated category, see [category]_exercise_names */ + categorySubtype?: Types.Uint16[]; + weightDisplayUnit?: Types.FitBaseUnit; + messageIndex?: Types.MessageIndex; + wktStepIndex?: Types.MessageIndex; +} + +export interface JumpMesg extends Mesg { + timestamp?: Types.DateTime; + distance?: Types.Float32; + height?: Types.Float32; + rotations?: Types.Uint8; + hangTime?: Types.Float32; + /** A score for a jump calculated based on hang time, rotations, and distance. */ + score?: Types.Float32; + positionLat?: Types.Sint32; + positionLong?: Types.Sint32; + speed?: Types.Float64; + enhancedSpeed?: Types.Float64; +} + +export interface SplitMesg extends Mesg { + messageIndex?: Types.MessageIndex; + splitType?: Types.SplitType; + totalElapsedTime?: Types.Float64; + totalTimerTime?: Types.Float64; + totalDistance?: Types.Float64; + avgSpeed?: Types.Float64; + startTime?: Types.DateTime; + totalAscent?: Types.Uint16; + totalDescent?: Types.Uint16; + startPositionLat?: Types.Sint32; + startPositionLong?: Types.Sint32; + endPositionLat?: Types.Sint32; + endPositionLong?: Types.Sint32; + maxSpeed?: Types.Float64; + avgVertSpeed?: Types.Float64; + endTime?: Types.DateTime; + totalCalories?: Types.Uint32; + startElevation?: Types.Float64; + /** Active time of split rounds */ + activeTime?: Types.Float64; + totalMovingTime?: Types.Float64; +} + +export interface SplitSummaryMesg extends Mesg { + messageIndex?: Types.MessageIndex; + splitType?: Types.SplitType; + numSplits?: Types.Uint16; + totalTimerTime?: Types.Float64; + totalDistance?: Types.Float64; + avgSpeed?: Types.Float64; + maxSpeed?: Types.Float64; + totalAscent?: Types.Uint16; + totalDescent?: Types.Uint16; + avgHeartRate?: Types.Uint8; + maxHeartRate?: Types.Uint8; + avgVertSpeed?: Types.Float64; + totalCalories?: Types.Uint32; + /** total active time in all split rounds */ + activeTime?: Types.Float64; + totalMovingTime?: Types.Float64; +} + +export interface ClimbProMesg extends Mesg { + timestamp?: Types.DateTime; + positionLat?: Types.Sint32; + positionLong?: Types.Sint32; + climbProEvent?: Types.ClimbProEvent; + climbNumber?: Types.Uint16; + climbCategory?: Types.Uint8; + currentDist?: Types.Float32; +} + +/** + * Must be logged before developer field is used + */ +export interface FieldDescriptionMesg extends Mesg { + developerDataIndex?: Types.Uint8; + fieldDefinitionNumber?: Types.Uint8; + fitBaseTypeId?: Types.FitBaseType; + fieldName?: Types.String; + array?: Types.Uint8; + components?: Types.String; + scale?: Types.Uint8; + offset?: Types.Sint8; + units?: Types.String; + bits?: Types.String; + accumulate?: Types.String; + fitBaseUnitId?: Types.FitBaseUnit; + nativeMesgNum?: Types.MesgNum; + nativeFieldNum?: Types.Uint8; +} + +/** + * Must be logged before field description + */ +export interface DeveloperDataIdMesg extends Mesg { + developerId?: Types.Byte[]; + applicationId?: Types.Byte[]; + manufacturerId?: Types.Manufacturer; + developerDataIndex?: Types.Uint8; + applicationVersion?: Types.Uint32; +} + +export interface CourseMesg extends Mesg { + sport?: Types.Sport; + name?: Types.String; + capabilities?: Types.CourseCapabilities; + subSport?: Types.SubSport; +} + +export interface CoursePointMesg extends Mesg { + messageIndex?: Types.MessageIndex; + timestamp?: Types.DateTime; + positionLat?: Types.Sint32; + positionLong?: Types.Sint32; + distance?: Types.Float64; + type?: Types.CoursePoint; + name?: Types.String; + favorite?: Types.Bool; +} + +/** + * Unique Identification data for a segment file + */ +export interface SegmentIdMesg extends Mesg { + /** Friendly name assigned to segment */ + name?: Types.String; + /** UUID of the segment */ + uuid?: Types.String; + /** Sport associated with the segment */ + sport?: Types.Sport; + /** Segment enabled for evaluation */ + enabled?: Types.Bool; + /** Primary key of the user that created the segment */ + userProfilePrimaryKey?: Types.Uint32; + /** ID of the device that created the segment */ + deviceId?: Types.Uint32; + /** Index for the Leader Board entry selected as the default race participant */ + defaultRaceLeader?: Types.Uint8; + /** Indicates if any segments should be deleted */ + deleteStatus?: Types.SegmentDeleteStatus; + /** Indicates how the segment was selected to be sent to the device */ + selectionType?: Types.SegmentSelectionType; +} + +/** + * Unique Identification data for an individual segment leader within a segment file + */ +export interface SegmentLeaderboardEntryMesg extends Mesg { + messageIndex?: Types.MessageIndex; + /** Friendly name assigned to leader */ + name?: Types.String; + /** Leader classification */ + type?: Types.SegmentLeaderboardType; + /** Primary user ID of this leader */ + groupPrimaryKey?: Types.Uint32; + /** ID of the activity associated with this leader time */ + activityId?: Types.Uint32; + /** Segment Time (includes pauses) */ + segmentTime?: Types.Float64; + /** String version of the activity_id. 21 characters long, express in decimal */ + activityIdString?: Types.String; +} + +/** + * Navigation and race evaluation point for a segment decribing a point along the segment path and time it took each segment leader to reach that point + */ +export interface SegmentPointMesg extends Mesg { + messageIndex?: Types.MessageIndex; + positionLat?: Types.Sint32; + positionLong?: Types.Sint32; + /** Accumulated distance along the segment at the described point */ + distance?: Types.Float64; + /** Accumulated altitude along the segment at the described point */ + altitude?: Types.Float64; + /** Accumualted time each leader board member required to reach the described point. This value is zero for all leader board members at the starting point of the segment. */ + leaderTime?: Types.Float64[]; + /** Accumulated altitude along the segment at the described point */ + enhancedAltitude?: Types.Float64; +} + +export interface SegmentLapMesg extends Mesg { + messageIndex?: Types.MessageIndex; + timestamp?: Types.DateTime; + event?: Types.Event; + eventType?: Types.EventType; + startTime?: Types.DateTime; + startPositionLat?: Types.Sint32; + startPositionLong?: Types.Sint32; + endPositionLat?: Types.Sint32; + endPositionLong?: Types.Sint32; + /** Time (includes pauses) */ + totalElapsedTime?: Types.Float64; + /** Timer Time (excludes pauses) */ + totalTimerTime?: Types.Float64; + totalDistance?: Types.Float64; + totalCycles?: Types.Uint32; + totalStrokes?: Types.Uint32; + totalCalories?: Types.Uint16; + /** If New Leaf */ + totalFatCalories?: Types.Uint16; + avgSpeed?: Types.Float64; + maxSpeed?: Types.Float64; + avgHeartRate?: Types.Uint8; + maxHeartRate?: Types.Uint8; + /** total_cycles / total_timer_time if non_zero_avg_cadence otherwise total_cycles / total_elapsed_time */ + avgCadence?: Types.Uint8; + maxCadence?: Types.Uint8; + /** total_power / total_timer_time if non_zero_avg_power otherwise total_power / total_elapsed_time */ + avgPower?: Types.Uint16; + maxPower?: Types.Uint16; + totalAscent?: Types.Uint16; + totalDescent?: Types.Uint16; + sport?: Types.Sport; + eventGroup?: Types.Uint8; + /** North east corner latitude. */ + necLat?: Types.Sint32; + /** North east corner longitude. */ + necLong?: Types.Sint32; + /** South west corner latitude. */ + swcLat?: Types.Sint32; + /** South west corner latitude. */ + swcLong?: Types.Sint32; + name?: Types.String; + normalizedPower?: Types.Uint16; + leftRightBalance?: Types.LeftRightBalance100; + subSport?: Types.SubSport; + totalWork?: Types.Uint32; + avgAltitude?: Types.Float64; + maxAltitude?: Types.Float64; + gpsAccuracy?: Types.Uint8; + avgGrade?: Types.Float64; + avgPosGrade?: Types.Float64; + avgNegGrade?: Types.Float64; + maxPosGrade?: Types.Float64; + maxNegGrade?: Types.Float64; + avgTemperature?: Types.Sint8; + maxTemperature?: Types.Sint8; + totalMovingTime?: Types.Float64; + avgPosVerticalSpeed?: Types.Float64; + avgNegVerticalSpeed?: Types.Float64; + maxPosVerticalSpeed?: Types.Float64; + maxNegVerticalSpeed?: Types.Float64; + timeInHrZone?: Types.Float64[]; + timeInSpeedZone?: Types.Float64[]; + timeInCadenceZone?: Types.Float64[]; + timeInPowerZone?: Types.Float64[]; + repetitionNum?: Types.Uint16; + minAltitude?: Types.Float64; + minHeartRate?: Types.Uint8; + activeTime?: Types.Float64; + wktStepIndex?: Types.MessageIndex; + sportEvent?: Types.SportEvent; + avgLeftTorqueEffectiveness?: Types.Float64; + avgRightTorqueEffectiveness?: Types.Float64; + avgLeftPedalSmoothness?: Types.Float64; + avgRightPedalSmoothness?: Types.Float64; + avgCombinedPedalSmoothness?: Types.Float64; + status?: Types.SegmentLapStatus; + uuid?: Types.String; + /** fractional part of the avg_cadence */ + avgFractionalCadence?: Types.Float64; + /** fractional part of the max_cadence */ + maxFractionalCadence?: Types.Float64; + /** fractional part of the total_cycles */ + totalFractionalCycles?: Types.Float64; + frontGearShiftCount?: Types.Uint16; + rearGearShiftCount?: Types.Uint16; + /** Total time spent in the standing position */ + timeStanding?: Types.Float64; + /** Number of transitions to the standing state */ + standCount?: Types.Uint16; + /** Average left platform center offset */ + avgLeftPco?: Types.Sint8; + /** Average right platform center offset */ + avgRightPco?: Types.Sint8; + /** Average left power phase angles. Data value indexes defined by power_phase_type. */ + avgLeftPowerPhase?: Types.Float64[]; + /** Average left power phase peak angles. Data value indexes defined by power_phase_type. */ + avgLeftPowerPhasePeak?: Types.Float64[]; + /** Average right power phase angles. Data value indexes defined by power_phase_type. */ + avgRightPowerPhase?: Types.Float64[]; + /** Average right power phase peak angles. Data value indexes defined by power_phase_type. */ + avgRightPowerPhasePeak?: Types.Float64[]; + /** Average power by position. Data value indexes defined by rider_position_type. */ + avgPowerPosition?: Types.Uint16[]; + /** Maximum power by position. Data value indexes defined by rider_position_type. */ + maxPowerPosition?: Types.Uint16[]; + /** Average cadence by position. Data value indexes defined by rider_position_type. */ + avgCadencePosition?: Types.Uint8[]; + /** Maximum cadence by position. Data value indexes defined by rider_position_type. */ + maxCadencePosition?: Types.Uint8[]; + /** Manufacturer that produced the segment */ + manufacturer?: Types.Manufacturer; + /** The grit score estimates how challenging a route could be for a cyclist in terms of time spent going over sharp turns or large grade slopes. */ + totalGrit?: Types.Float32; + /** The flow score estimates how long distance wise a cyclist deaccelerates over intervals where deacceleration is unnecessary such as smooth turns or small grade angle intervals. */ + totalFlow?: Types.Float32; + /** The grit score estimates how challenging a route could be for a cyclist in terms of time spent going over sharp turns or large grade slopes. */ + avgGrit?: Types.Float32; + /** The flow score estimates how long distance wise a cyclist deaccelerates over intervals where deacceleration is unnecessary such as smooth turns or small grade angle intervals. */ + avgFlow?: Types.Float32; + /** fractional part of total_ascent */ + totalFractionalAscent?: Types.Float64; + /** fractional part of total_descent */ + totalFractionalDescent?: Types.Float64; + enhancedAvgAltitude?: Types.Float64; + enhancedMaxAltitude?: Types.Float64; + enhancedMinAltitude?: Types.Float64; +} + +/** + * Summary of the unique segment and leaderboard information associated with a segment file. This message is used to compile a segment list file describing all segment files on a device. The segment list file is used when refreshing the contents of a segment file with the latest available leaderboard information. + */ +export interface SegmentFileMesg extends Mesg { + messageIndex?: Types.MessageIndex; + /** UUID of the segment file */ + fileUuid?: Types.String; + /** Enabled state of the segment file */ + enabled?: Types.Bool; + /** Primary key of the user that created the segment file */ + userProfilePrimaryKey?: Types.Uint32; + /** Leader type of each leader in the segment file */ + leaderType?: Types.SegmentLeaderboardType[]; + /** Group primary key of each leader in the segment file */ + leaderGroupPrimaryKey?: Types.Uint32[]; + /** Activity ID of each leader in the segment file */ + leaderActivityId?: Types.Uint32[]; + /** String version of the activity ID of each leader in the segment file. 21 characters long for each ID, express in decimal */ + leaderActivityIdString?: Types.String; + /** Index for the Leader Board entry selected as the default race participant */ + defaultRaceLeader?: Types.Uint8; +} + +export interface WorkoutMesg extends Mesg { + messageIndex?: Types.MessageIndex; + sport?: Types.Sport; + capabilities?: Types.WorkoutCapabilities; + /** number of valid steps */ + numValidSteps?: Types.Uint16; + wktName?: Types.String; + subSport?: Types.SubSport; + poolLength?: Types.Float64; + poolLengthUnit?: Types.DisplayMeasure; + /** Description of the workout */ + wktDescription?: Types.String; +} + +export interface WorkoutSessionMesg extends Mesg { + messageIndex?: Types.MessageIndex; + sport?: Types.Sport; + subSport?: Types.SubSport; + numValidSteps?: Types.Uint16; + firstStepIndex?: Types.Uint16; + poolLength?: Types.Float64; + poolLengthUnit?: Types.DisplayMeasure; +} + +export interface WorkoutStepMesg extends Mesg { + messageIndex?: Types.MessageIndex; + wktStepName?: Types.String; + durationType?: Types.WktStepDuration; + durationValue?: Types.Uint32; + durationTime?: Types.Float64; + durationDistance?: Types.Float64; + durationHr?: Types.WorkoutHr; + durationCalories?: Types.Uint32; + /** message_index of step to loop back to. Steps are assumed to be in the order by message_index. custom_name and intensity members are undefined for this duration type.*/ + durationStep?: Types.Uint32; + durationPower?: Types.WorkoutPower; + durationReps?: Types.Uint32; + targetType?: Types.WktStepTarget; + targetValue?: Types.Uint32; + /** speed zone (1-10);Custom =0;*/ + targetSpeedZone?: Types.Uint32; + /** hr zone (1-5);Custom =0;*/ + targetHrZone?: Types.Uint32; + /** Zone (1-?); Custom = 0;*/ + targetCadenceZone?: Types.Uint32; + /** Power Zone ( 1-7); Custom = 0;*/ + targetPowerZone?: Types.Uint32; + /** # of repetitions*/ + repeatSteps?: Types.Uint32; + repeatTime?: Types.Float64; + repeatDistance?: Types.Float64; + repeatCalories?: Types.Uint32; + repeatHr?: Types.WorkoutHr; + repeatPower?: Types.WorkoutPower; + targetStrokeType?: Types.SwimStroke; + customTargetValueLow?: Types.Uint32; + customTargetSpeedLow?: Types.Float64; + customTargetHeartRateLow?: Types.WorkoutHr; + customTargetCadenceLow?: Types.Uint32; + customTargetPowerLow?: Types.WorkoutPower; + customTargetValueHigh?: Types.Uint32; + customTargetSpeedHigh?: Types.Float64; + customTargetHeartRateHigh?: Types.WorkoutHr; + customTargetCadenceHigh?: Types.Uint32; + customTargetPowerHigh?: Types.WorkoutPower; + intensity?: Types.Intensity; + notes?: Types.String; + equipment?: Types.WorkoutEquipment; + exerciseCategory?: Types.ExerciseCategory; + exerciseName?: Types.Uint16; + exerciseWeight?: Types.Float64; + weightDisplayUnit?: Types.FitBaseUnit; + secondaryTargetType?: Types.WktStepTarget; + secondaryTargetValue?: Types.Uint32; + /** speed zone (1-10);Custom =0;*/ + secondaryTargetSpeedZone?: Types.Uint32; + /** hr zone (1-5);Custom =0;*/ + secondaryTargetHrZone?: Types.Uint32; + /** Zone (1-?); Custom = 0;*/ + secondaryTargetCadenceZone?: Types.Uint32; + /** Power Zone ( 1-7); Custom = 0;*/ + secondaryTargetPowerZone?: Types.Uint32; + secondaryTargetStrokeType?: Types.SwimStroke; + secondaryCustomTargetValueLow?: Types.Uint32; + secondaryCustomTargetSpeedLow?: Types.Float64; + secondaryCustomTargetHeartRateLow?: Types.WorkoutHr; + secondaryCustomTargetCadenceLow?: Types.Uint32; + secondaryCustomTargetPowerLow?: Types.WorkoutPower; + secondaryCustomTargetValueHigh?: Types.Uint32; + secondaryCustomTargetSpeedHigh?: Types.Float64; + secondaryCustomTargetHeartRateHigh?: Types.WorkoutHr; + secondaryCustomTargetCadenceHigh?: Types.Uint32; + secondaryCustomTargetPowerHigh?: Types.WorkoutPower; +} + +export interface ExerciseTitleMesg extends Mesg { + messageIndex?: Types.MessageIndex; + exerciseCategory?: Types.ExerciseCategory; + exerciseName?: Types.Uint16; + wktStepName?: Types.String; +} + +export interface ScheduleMesg extends Mesg { + /** Corresponds to file_id of scheduled workout / course. */ + manufacturer?: Types.Manufacturer; + /** Corresponds to file_id of scheduled workout / course. */ + product?: Types.Uint16; + faveroProduct?: Types.FaveroProduct; + garminProduct?: Types.GarminProduct; + /** Corresponds to file_id of scheduled workout / course. */ + serialNumber?: Types.Uint32z; + /** Corresponds to file_id of scheduled workout / course. */ + timeCreated?: Types.DateTime; + /** TRUE if this activity has been started */ + completed?: Types.Bool; + type?: Types.Schedule; + scheduledTime?: Types.LocalDateTime; +} + +export interface TotalsMesg extends Mesg { + messageIndex?: Types.MessageIndex; + timestamp?: Types.DateTime; + /** Excludes pauses */ + timerTime?: Types.Uint32; + distance?: Types.Uint32; + calories?: Types.Uint32; + sport?: Types.Sport; + /** Includes pauses */ + elapsedTime?: Types.Uint32; + sessions?: Types.Uint16; + activeTime?: Types.Uint32; + sportIndex?: Types.Uint8; +} + +export interface WeightScaleMesg extends Mesg { + timestamp?: Types.DateTime; + weight?: Types.Float64; + percentFat?: Types.Float64; + percentHydration?: Types.Float64; + visceralFatMass?: Types.Float64; + boneMass?: Types.Float64; + muscleMass?: Types.Float64; + basalMet?: Types.Float64; + physiqueRating?: Types.Uint8; + /** ~4kJ per kcal, 0.25 allows max 16384 kcal */ + activeMet?: Types.Float64; + metabolicAge?: Types.Uint8; + visceralFatRating?: Types.Uint8; + /** Associates this weight scale message to a user. This corresponds to the index of the user profile message in the weight scale file. */ + userProfileIndex?: Types.MessageIndex; + bmi?: Types.Float64; +} + +export interface BloodPressureMesg extends Mesg { + timestamp?: Types.DateTime; + systolicPressure?: Types.Uint16; + diastolicPressure?: Types.Uint16; + meanArterialPressure?: Types.Uint16; + map3SampleMean?: Types.Uint16; + mapMorningValues?: Types.Uint16; + mapEveningValues?: Types.Uint16; + heartRate?: Types.Uint8; + heartRateType?: Types.HrType; + status?: Types.BpStatus; + /** Associates this blood pressure message to a user. This corresponds to the index of the user profile message in the blood pressure file. */ + userProfileIndex?: Types.MessageIndex; +} + +export interface MonitoringInfoMesg extends Mesg { + timestamp?: Types.DateTime; + /** Use to convert activity timestamps to local time if device does not support time zone and daylight savings time correction. */ + localTimestamp?: Types.LocalDateTime; + activityType?: Types.ActivityType[]; + /** Indexed by activity_type */ + cyclesToDistance?: Types.Float64[]; + /** Indexed by activity_type */ + cyclesToCalories?: Types.Float64[]; + restingMetabolicRate?: Types.Uint16; +} + +export interface MonitoringMesg extends Mesg { + /** Must align to logging interval, for example, time must be 00:00:00 for daily log. */ + timestamp?: Types.DateTime; + /** Associates this data to device_info message. Not required for file with single device (sensor). */ + deviceIndex?: Types.DeviceIndex; + /** Accumulated total calories. Maintained by MonitoringReader for each activity_type. See SDK documentation */ + calories?: Types.Uint16; + /** Accumulated distance. Maintained by MonitoringReader for each activity_type. See SDK documentation. */ + distance?: Types.Float64; + /** Accumulated cycles. Maintained by MonitoringReader for each activity_type. See SDK documentation. */ + cycles?: Types.Float64; + steps?: Types.Uint32; + strokes?: Types.Float64; + activeTime?: Types.Float64; + activityType?: Types.ActivityType; + activitySubtype?: Types.ActivitySubtype; + activityLevel?: Types.ActivityLevel; + distance16?: Types.Uint16; + cycles16?: Types.Uint16; + activeTime16?: Types.Uint16; + /** Must align to logging interval, for example, time must be 00:00:00 for daily log. */ + localTimestamp?: Types.LocalDateTime; + /** Avg temperature during the logging interval ended at timestamp */ + temperature?: Types.Float64; + /** Min temperature during the logging interval ended at timestamp */ + temperatureMin?: Types.Float64; + /** Max temperature during the logging interval ended at timestamp */ + temperatureMax?: Types.Float64; + /** Indexed using minute_activity_level enum */ + activityTime?: Types.Uint16[]; + activeCalories?: Types.Uint16; + /** Indicates single type / intensity for duration since last monitoring message. */ + currentActivityTypeIntensity?: Types.Byte; + timestampMin8?: Types.Uint8; + timestamp16?: Types.Uint16; + heartRate?: Types.Uint8; + intensity?: Types.Float64; + durationMin?: Types.Uint16; + duration?: Types.Uint32; + ascent?: Types.Float64; + descent?: Types.Float64; + moderateActivityMinutes?: Types.Uint16; + vigorousActivityMinutes?: Types.Uint16; +} + +export interface MonitoringHrDataMesg extends Mesg { + /** Must align to logging interval, for example, time must be 00:00:00 for daily log. */ + timestamp?: Types.DateTime; + /** 7-day rolling average */ + restingHeartRate?: Types.Uint8; + /** RHR for today only. (Feeds into 7-day average) */ + currentDayRestingHeartRate?: Types.Uint8; +} + +export interface Spo2DataMesg extends Mesg { + timestamp?: Types.DateTime; + readingSpo2?: Types.Uint8; + readingConfidence?: Types.Uint8; + /** Mode when data was captured */ + mode?: Types.Spo2MeasurementType; +} + +export interface HrMesg extends Mesg { + timestamp?: Types.DateTime; + fractionalTimestamp?: Types.Float64; + time256?: Types.Float64; + filteredBpm?: Types.Uint8[]; + eventTimestamp?: Types.Float64[]; + eventTimestamp12?: Types.Byte[]; +} + +/** + * Value from 1 to 100 calculated by FirstBeat + */ +export interface StressLevelMesg extends Mesg { + stressLevelValue?: Types.Sint16; + /** Time stress score was calculated */ + stressLevelTime?: Types.DateTime; +} + +export interface MaxMetDataMesg extends Mesg { + /** Time maxMET and vo2 were calculated */ + updateTime?: Types.DateTime; + vo2Max?: Types.Float64; + sport?: Types.Sport; + subSport?: Types.SubSport; + maxMetCategory?: Types.MaxMetCategory; + /** Indicates if calibrated data was used in the calculation */ + calibratedData?: Types.Bool; + /** Indicates if the estimate was obtained using a chest strap or wrist heart rate */ + hrSource?: Types.MaxMetHeartRateSource; + /** Indidcates if the estimate was obtained using onboard GPS or connected GPS */ + speedSource?: Types.MaxMetSpeedSource; +} + +/** + * Body battery data used for HSA custom data logging + */ +export interface HsaBodyBatteryDataMesg extends Mesg { + timestamp?: Types.DateTime; + /** Processing interval length in seconds */ + processingInterval?: Types.Uint16; + /** Body battery level: [0,100] Blank: -16 */ + level?: Types.Sint8[]; + /** Body battery charged value */ + charged?: Types.Sint16[]; + /** Body battery uncharged value */ + uncharged?: Types.Sint16[]; +} + +/** + * HSA events + */ +export interface HsaEventMesg extends Mesg { + timestamp?: Types.DateTime; + /** Event ID. Health SDK use only */ + eventId?: Types.Uint8; +} + +/** + * Raw accelerometer data used for HSA custom data logging + */ +export interface HsaAccelerometerDataMesg extends Mesg { + timestamp?: Types.DateTime; + /** Millisecond resolution of the timestamp */ + timestampMs?: Types.Uint16; + /** Sampling Interval in Milliseconds */ + samplingInterval?: Types.Uint16; + /** X-Axis Measurement */ + accelX?: Types.Float64[]; + /** Y-Axis Measurement */ + accelY?: Types.Float64[]; + /** Z-Axis Measurement */ + accelZ?: Types.Float64[]; + /** 32 kHz timestamp */ + timestamp32k?: Types.Uint32; +} + +export interface HsaGyroscopeDataMesg extends Mesg { + timestamp?: Types.DateTime; + /** Millisecond resolution of the timestamp */ + timestampMs?: Types.Uint16; + /** Sampling Interval in 32 kHz timescale */ + samplingInterval?: Types.Uint16; + /** X-Axis Measurement */ + gyroX?: Types.Float64[]; + /** Y-Axis Measurement */ + gyroY?: Types.Float64[]; + /** Z-Axis Measurement */ + gyroZ?: Types.Float64[]; + /** 32 kHz timestamp */ + timestamp32k?: Types.Uint32; +} + +/** + * User's current daily step data used for HSA custom data logging + */ +export interface HsaStepDataMesg extends Mesg { + timestamp?: Types.DateTime; + /** Processing interval length in seconds. File start: 0xFFFFFFEF File stop: 0xFFFFFFEE */ + processingInterval?: Types.Uint16; + /** Total step sum */ + steps?: Types.Uint32[]; +} + +/** + * User's current SpO2 data used for HSA custom data logging + */ +export interface HsaSpo2DataMesg extends Mesg { + timestamp?: Types.DateTime; + /** Processing interval length in seconds */ + processingInterval?: Types.Uint16; + /** SpO2 Reading: [70,100] Blank: 240 */ + readingSpo2?: Types.Uint8[]; + /** SpO2 Confidence: [0,254] */ + confidence?: Types.Uint8[]; +} + +/** + * User's current stress data used for HSA custom data logging + */ +export interface HsaStressDataMesg extends Mesg { + timestamp?: Types.DateTime; + /** Processing interval length in seconds */ + processingInterval?: Types.Uint16; + /** Stress Level: [0,100] Off wrist: -1 Excess motion: -2 Not enough data: -3 Recovering from exercise: -4 Unidentified: -5 Blank: -16 */ + stressLevel?: Types.Sint8[]; +} + +/** + * User's current respiration data used for HSA custom data logging + */ +export interface HsaRespirationDataMesg extends Mesg { + timestamp?: Types.DateTime; + /** Processing interval length in seconds */ + processingInterval?: Types.Uint16; + /** Breaths / min: [1,100] Invalid: 255 Excess motion: 254 Off wrist: 253 Not available: 252 Blank: 2.4 */ + respirationRate?: Types.Float64[]; +} + +/** + * User's current heart rate data used for HSA custom data logging + */ +export interface HsaHeartRateDataMesg extends Mesg { + timestamp?: Types.DateTime; + /** Processing interval length in seconds */ + processingInterval?: Types.Uint16; + /** Status of measurements in buffer - 0 indicates SEARCHING 1 indicates LOCKED */ + status?: Types.Uint8; + /** Beats / min. Blank: 0 */ + heartRate?: Types.Uint8[]; +} + +/** + * Configuration data for HSA custom data logging + */ +export interface HsaConfigurationDataMesg extends Mesg { + /** Encoded configuration data */ + timestamp?: Types.DateTime; + /** Encoded configuration data. Health SDK use only */ + data?: Types.Byte[]; + /** Size in bytes of data field */ + dataSize?: Types.Uint8; +} + +/** + * Wrist temperature data used for HSA custom data logging + */ +export interface HsaWristTemperatureDataMesg extends Mesg { + timestamp?: Types.DateTime; + /** Processing interval length in seconds */ + processingInterval?: Types.Uint16; + /** Wrist temperature reading */ + value?: Types.Float64[]; +} + +export interface MemoGlobMesg extends Mesg { + /** Sequence number of memo blocks */ + partIndex?: Types.Uint32; + /** Deprecated. Use data field. */ + memo?: Types.Byte[]; + /** Message Number of the parent message */ + mesgNum?: Types.MesgNum; + /** Index of mesg that this glob is associated with. */ + parentIndex?: Types.MessageIndex; + /** Field within the parent that this glob is associated with */ + fieldNum?: Types.Uint8; + /** Block of utf8 bytes. Note, mutltibyte characters may be split across adjoining memo_glob messages. */ + data?: Types.Uint8z[]; +} + +export interface SleepLevelMesg extends Mesg { + timestamp?: Types.DateTime; + sleepLevel?: Types.SleepLevel; +} + +export interface AntChannelIdMesg extends Mesg { + channelNumber?: Types.Uint8; + deviceType?: Types.Uint8z; + deviceNumber?: Types.Uint16z; + transmissionType?: Types.Uint8z; + deviceIndex?: Types.DeviceIndex; +} + +export interface AntRxMesg extends Mesg { + timestamp?: Types.DateTime; + fractionalTimestamp?: Types.Float64; + mesgId?: Types.Byte; + mesgData?: Types.Byte[]; + channelNumber?: Types.Uint8; + data?: Types.Byte[]; +} + +export interface AntTxMesg extends Mesg { + timestamp?: Types.DateTime; + fractionalTimestamp?: Types.Float64; + mesgId?: Types.Byte; + mesgData?: Types.Byte[]; + channelNumber?: Types.Uint8; + data?: Types.Byte[]; +} + +export interface ExdScreenConfigurationMesg extends Mesg { + screenIndex?: Types.Uint8; + /** number of fields in screen */ + fieldCount?: Types.Uint8; + layout?: Types.ExdLayout; + screenEnabled?: Types.Bool; +} + +export interface ExdDataFieldConfigurationMesg extends Mesg { + screenIndex?: Types.Uint8; + conceptField?: Types.Byte; + fieldId?: Types.Uint8; + conceptCount?: Types.Uint8; + displayType?: Types.ExdDisplayType; + title?: Types.String; +} + +export interface ExdDataConceptConfigurationMesg extends Mesg { + screenIndex?: Types.Uint8; + conceptField?: Types.Byte; + fieldId?: Types.Uint8; + conceptIndex?: Types.Uint8; + dataPage?: Types.Uint8; + conceptKey?: Types.Uint8; + scaling?: Types.Uint8; + dataUnits?: Types.ExdDataUnits; + qualifier?: Types.ExdQualifiers; + descriptor?: Types.ExdDescriptors; + isSigned?: Types.Bool; +} + +export interface DiveSummaryMesg extends Mesg { + timestamp?: Types.DateTime; + referenceMesg?: Types.MesgNum; + referenceIndex?: Types.MessageIndex; + /** 0 if above water */ + avgDepth?: Types.Float64; + /** 0 if above water */ + maxDepth?: Types.Float64; + /** Time since end of last dive */ + surfaceInterval?: Types.Uint32; + startCns?: Types.Uint8; + endCns?: Types.Uint8; + startN2?: Types.Uint16; + endN2?: Types.Uint16; + o2Toxicity?: Types.Uint16; + diveNumber?: Types.Uint32; + bottomTime?: Types.Float64; + /** Average pressure-based surface air consumption */ + avgPressureSac?: Types.Float64; + /** Average volumetric surface air consumption */ + avgVolumeSac?: Types.Float64; + /** Average respiratory minute volume */ + avgRmv?: Types.Float64; + /** Time to reach deepest level stop */ + descentTime?: Types.Float64; + /** Time after leaving bottom until reaching surface */ + ascentTime?: Types.Float64; + /** Average ascent rate, not including descents or stops */ + avgAscentRate?: Types.Float64; + /** Average descent rate, not including ascents or stops */ + avgDescentRate?: Types.Float64; + /** Maximum ascent rate */ + maxAscentRate?: Types.Float64; + /** Maximum descent rate */ + maxDescentRate?: Types.Float64; + /** Time spent neither ascending nor descending */ + hangTime?: Types.Float64; +} + +/** + * Number of acclerometer zero crossings summed over the specified time interval + */ +export interface AadAccelFeaturesMesg extends Mesg { + timestamp?: Types.DateTime; + /** Time interval length in seconds */ + time?: Types.Uint16; + /** Total accelerometer energy in the interval */ + energyTotal?: Types.Uint32; + /** Count of zero crossings */ + zeroCrossCnt?: Types.Uint16; + /** Instance ID of zero crossing algorithm */ + instance?: Types.Uint8; + /** Total accelerometer time above threshold in the interval */ + timeAboveThreshold?: Types.Float64; +} + +/** + * Heart rate variability + */ +export interface HrvMesg extends Mesg { + /** Time between beats */ + time?: Types.Float64[]; +} + +/** + * Array of heart beat intervals + */ +export interface BeatIntervalsMesg extends Mesg { + timestamp?: Types.DateTime; + /** Milliseconds past date_time */ + timestampMs?: Types.Uint16; + /** Array of millisecond times between beats */ + time?: Types.Uint16[]; +} + +export interface HrvStatusSummaryMesg extends Mesg { + timestamp?: Types.DateTime; + /** 7 day RMSSD average over sleep */ + weeklyAverage?: Types.Float64; + /** Last night RMSSD average over sleep */ + lastNightAverage?: Types.Float64; + /** 5 minute high RMSSD value over sleep */ + lastNight5MinHigh?: Types.Float64; + /** 3 week baseline, upper boundary of low HRV status */ + baselineLowUpper?: Types.Float64; + /** 3 week baseline, lower boundary of balanced HRV status */ + baselineBalancedLower?: Types.Float64; + /** 3 week baseline, upper boundary of balanced HRV status */ + baselineBalancedUpper?: Types.Float64; + status?: Types.HrvStatus; +} + +export interface HrvValueMesg extends Mesg { + timestamp?: Types.DateTime; + /** 5 minute RMSSD */ + value?: Types.Float64; +} + +/** + * Raw Beat-to-Beat Interval values + */ +export interface RawBbiMesg extends Mesg { + timestamp?: Types.DateTime; + /** Millisecond resolution of the timestamp */ + timestampMs?: Types.Uint16; + /** 1 bit for gap indicator, 1 bit for quality indicator, and 14 bits for Beat-to-Beat interval values in whole-integer millisecond resolution */ + data?: Types.Uint16[]; + /** Array of millisecond times between beats */ + time?: Types.Uint16[]; + /** 1 = high confidence. 0 = low confidence. N/A when gap = 1 */ + quality?: Types.Uint8[]; + /** 1 = gap (time represents ms gap length). 0 = BBI data */ + gap?: Types.Uint8[]; +} + +export interface RespirationRateMesg extends Mesg { + timestamp?: Types.DateTime; + /** Breaths * 100 /min, -300 indicates invalid, -200 indicates large motion, -100 indicates off wrist */ + respirationRate?: Types.Float64; +} + +/** + * Specifically used for XERO products. + */ +export interface ChronoShotSessionMesg extends Mesg { + timestamp?: Types.DateTime; + minSpeed?: Types.Float64; + maxSpeed?: Types.Float64; + avgSpeed?: Types.Float64; + shotCount?: Types.Uint16; + projectileType?: Types.ProjectileType; + grainWeight?: Types.Float64; + standardDeviation?: Types.Float64; +} + +/** + * Specifically used for XERO products. + */ +export interface ChronoShotDataMesg extends Mesg { + timestamp?: Types.DateTime; + shotSpeed?: Types.Float64; + shotNum?: Types.Uint16; +} + +export interface TankUpdateMesg extends Mesg { + timestamp?: Types.DateTime; + sensor?: Types.AntChannelId; + pressure?: Types.Float64; +} + +export interface TankSummaryMesg extends Mesg { + timestamp?: Types.DateTime; + sensor?: Types.AntChannelId; + startPressure?: Types.Float64; + endPressure?: Types.Float64; + volumeUsed?: Types.Float64; +} + +export interface SleepAssessmentMesg extends Mesg { + /** Average of awake_time_score and awakenings_count_score. If valid: 0 (worst) to 100 (best). If unknown: FIT_UINT8_INVALID. */ + combinedAwakeScore?: Types.Uint8; + /** Score that evaluates the total time spent awake between sleep. If valid: 0 (worst) to 100 (best). If unknown: FIT_UINT8_INVALID. */ + awakeTimeScore?: Types.Uint8; + /** Score that evaluates the number of awakenings that interrupt sleep. If valid: 0 (worst) to 100 (best). If unknown: FIT_UINT8_INVALID. */ + awakeningsCountScore?: Types.Uint8; + /** Score that evaluates the amount of deep sleep. If valid: 0 (worst) to 100 (best). If unknown: FIT_UINT8_INVALID. */ + deepSleepScore?: Types.Uint8; + /** Score that evaluates the quality of sleep based on sleep stages, heart-rate variability and possible awakenings during the night. If valid: 0 (worst) to 100 (best). If unknown: FIT_UINT8_INVALID. */ + sleepDurationScore?: Types.Uint8; + /** Score that evaluates the amount of light sleep. If valid: 0 (worst) to 100 (best). If unknown: FIT_UINT8_INVALID. */ + lightSleepScore?: Types.Uint8; + /** Total score that summarizes the overall quality of sleep, combining sleep duration and quality. If valid: 0 (worst) to 100 (best). If unknown: FIT_UINT8_INVALID. */ + overallSleepScore?: Types.Uint8; + /** Score that evaluates the quality of sleep based on sleep stages, heart-rate variability and possible awakenings during the night. If valid: 0 (worst) to 100 (best). If unknown: FIT_UINT8_INVALID. */ + sleepQualityScore?: Types.Uint8; + /** Score that evaluates stress and recovery during sleep. If valid: 0 (worst) to 100 (best). If unknown: FIT_UINT8_INVALID. */ + sleepRecoveryScore?: Types.Uint8; + /** Score that evaluates the amount of REM sleep. If valid: 0 (worst) to 100 (best). If unknown: FIT_UINT8_INVALID. */ + remSleepScore?: Types.Uint8; + /** Score that evaluates the amount of restlessness during sleep. If valid: 0 (worst) to 100 (best). If unknown: FIT_UINT8_INVALID. */ + sleepRestlessnessScore?: Types.Uint8; + /** The number of awakenings during sleep. */ + awakeningsCount?: Types.Uint8; + /** Score that evaluates the sleep interruptions. If valid: 0 (worst) to 100 (best). If unknown: FIT_UINT8_INVALID. */ + interruptionsScore?: Types.Uint8; + /** Excludes stress during awake periods in the sleep window */ + averageStressDuringSleep?: Types.Float64; +} + +export interface SleepDisruptionSeverityPeriodMesg extends Mesg { + messageIndex?: Types.MessageIndex; + timestamp?: Types.DateTime; + severity?: Types.SleepDisruptionSeverity; +} + +export interface SleepDisruptionOvernightSeverityMesg extends Mesg { + timestamp?: Types.DateTime; + severity?: Types.SleepDisruptionSeverity; +} + +export interface NapEventMesg extends Mesg { + messageIndex?: Types.MessageIndex; + timestamp?: Types.DateTime; + startTime?: Types.DateTime; + startTimezoneOffset?: Types.Sint16; + endTime?: Types.DateTime; + endTimezoneOffset?: Types.Sint16; + feedback?: Types.NapPeriodFeedback; + isDeleted?: Types.Bool; + source?: Types.NapSource; + /** The timestamp representing when this nap event was last updated */ + updateTimestamp?: Types.DateTime; +} + +export interface SkinTempOvernightMesg extends Mesg { + timestamp?: Types.DateTime; + localTimestamp?: Types.LocalDateTime; + /** The average overnight deviation from baseline temperature in degrees C */ + averageDeviation?: Types.Float32; + /** The average 7 day overnight deviation from baseline temperature in degrees C */ + average7DayDeviation?: Types.Float32; + /** Final overnight temperature value */ + nightlyValue?: Types.Float32; +} + +export interface PadMesg extends Mesg { +} + +export interface FitMessages { + fileIdMesgs?: FileIdMesg[]; + fileCreatorMesgs?: FileCreatorMesg[]; + timestampCorrelationMesgs?: TimestampCorrelationMesg[]; + softwareMesgs?: SoftwareMesg[]; + slaveDeviceMesgs?: SlaveDeviceMesg[]; + capabilitiesMesgs?: CapabilitiesMesg[]; + fileCapabilitiesMesgs?: FileCapabilitiesMesg[]; + mesgCapabilitiesMesgs?: MesgCapabilitiesMesg[]; + fieldCapabilitiesMesgs?: FieldCapabilitiesMesg[]; + deviceSettingsMesgs?: DeviceSettingsMesg[]; + userProfileMesgs?: UserProfileMesg[]; + hrmProfileMesgs?: HrmProfileMesg[]; + sdmProfileMesgs?: SdmProfileMesg[]; + bikeProfileMesgs?: BikeProfileMesg[]; + connectivityMesgs?: ConnectivityMesg[]; + watchfaceSettingsMesgs?: WatchfaceSettingsMesg[]; + ohrSettingsMesgs?: OhrSettingsMesg[]; + timeInZoneMesgs?: TimeInZoneMesg[]; + zonesTargetMesgs?: ZonesTargetMesg[]; + sportMesgs?: SportMesg[]; + hrZoneMesgs?: HrZoneMesg[]; + speedZoneMesgs?: SpeedZoneMesg[]; + cadenceZoneMesgs?: CadenceZoneMesg[]; + powerZoneMesgs?: PowerZoneMesg[]; + metZoneMesgs?: MetZoneMesg[]; + trainingSettingsMesgs?: TrainingSettingsMesg[]; + diveSettingsMesgs?: DiveSettingsMesg[]; + diveAlarmMesgs?: DiveAlarmMesg[]; + diveApneaAlarmMesgs?: DiveApneaAlarmMesg[]; + diveGasMesgs?: DiveGasMesg[]; + goalMesgs?: GoalMesg[]; + activityMesgs?: ActivityMesg[]; + sessionMesgs?: SessionMesg[]; + lapMesgs?: LapMesg[]; + lengthMesgs?: LengthMesg[]; + recordMesgs?: RecordMesg[]; + eventMesgs?: EventMesg[]; + deviceInfoMesgs?: DeviceInfoMesg[]; + deviceAuxBatteryInfoMesgs?: DeviceAuxBatteryInfoMesg[]; + trainingFileMesgs?: TrainingFileMesg[]; + weatherConditionsMesgs?: WeatherConditionsMesg[]; + weatherAlertMesgs?: WeatherAlertMesg[]; + gpsMetadataMesgs?: GpsMetadataMesg[]; + cameraEventMesgs?: CameraEventMesg[]; + gyroscopeDataMesgs?: GyroscopeDataMesg[]; + accelerometerDataMesgs?: AccelerometerDataMesg[]; + magnetometerDataMesgs?: MagnetometerDataMesg[]; + barometerDataMesgs?: BarometerDataMesg[]; + threeDSensorCalibrationMesgs?: ThreeDSensorCalibrationMesg[]; + oneDSensorCalibrationMesgs?: OneDSensorCalibrationMesg[]; + videoFrameMesgs?: VideoFrameMesg[]; + obdiiDataMesgs?: ObdiiDataMesg[]; + nmeaSentenceMesgs?: NmeaSentenceMesg[]; + aviationAttitudeMesgs?: AviationAttitudeMesg[]; + videoMesgs?: VideoMesg[]; + videoTitleMesgs?: VideoTitleMesg[]; + videoDescriptionMesgs?: VideoDescriptionMesg[]; + videoClipMesgs?: VideoClipMesg[]; + setMesgs?: SetMesg[]; + jumpMesgs?: JumpMesg[]; + splitMesgs?: SplitMesg[]; + splitSummaryMesgs?: SplitSummaryMesg[]; + climbProMesgs?: ClimbProMesg[]; + fieldDescriptionMesgs?: FieldDescriptionMesg[]; + developerDataIdMesgs?: DeveloperDataIdMesg[]; + courseMesgs?: CourseMesg[]; + coursePointMesgs?: CoursePointMesg[]; + segmentIdMesgs?: SegmentIdMesg[]; + segmentLeaderboardEntryMesgs?: SegmentLeaderboardEntryMesg[]; + segmentPointMesgs?: SegmentPointMesg[]; + segmentLapMesgs?: SegmentLapMesg[]; + segmentFileMesgs?: SegmentFileMesg[]; + workoutMesgs?: WorkoutMesg[]; + workoutSessionMesgs?: WorkoutSessionMesg[]; + workoutStepMesgs?: WorkoutStepMesg[]; + exerciseTitleMesgs?: ExerciseTitleMesg[]; + scheduleMesgs?: ScheduleMesg[]; + totalsMesgs?: TotalsMesg[]; + weightScaleMesgs?: WeightScaleMesg[]; + bloodPressureMesgs?: BloodPressureMesg[]; + monitoringInfoMesgs?: MonitoringInfoMesg[]; + monitoringMesgs?: MonitoringMesg[]; + monitoringHrDataMesgs?: MonitoringHrDataMesg[]; + spo2DataMesgs?: Spo2DataMesg[]; + hrMesgs?: HrMesg[]; + stressLevelMesgs?: StressLevelMesg[]; + maxMetDataMesgs?: MaxMetDataMesg[]; + hsaBodyBatteryDataMesgs?: HsaBodyBatteryDataMesg[]; + hsaEventMesgs?: HsaEventMesg[]; + hsaAccelerometerDataMesgs?: HsaAccelerometerDataMesg[]; + hsaGyroscopeDataMesgs?: HsaGyroscopeDataMesg[]; + hsaStepDataMesgs?: HsaStepDataMesg[]; + hsaSpo2DataMesgs?: HsaSpo2DataMesg[]; + hsaStressDataMesgs?: HsaStressDataMesg[]; + hsaRespirationDataMesgs?: HsaRespirationDataMesg[]; + hsaHeartRateDataMesgs?: HsaHeartRateDataMesg[]; + hsaConfigurationDataMesgs?: HsaConfigurationDataMesg[]; + hsaWristTemperatureDataMesgs?: HsaWristTemperatureDataMesg[]; + memoGlobMesgs?: MemoGlobMesg[]; + sleepLevelMesgs?: SleepLevelMesg[]; + antChannelIdMesgs?: AntChannelIdMesg[]; + antRxMesgs?: AntRxMesg[]; + antTxMesgs?: AntTxMesg[]; + exdScreenConfigurationMesgs?: ExdScreenConfigurationMesg[]; + exdDataFieldConfigurationMesgs?: ExdDataFieldConfigurationMesg[]; + exdDataConceptConfigurationMesgs?: ExdDataConceptConfigurationMesg[]; + diveSummaryMesgs?: DiveSummaryMesg[]; + aadAccelFeaturesMesgs?: AadAccelFeaturesMesg[]; + hrvMesgs?: HrvMesg[]; + beatIntervalsMesgs?: BeatIntervalsMesg[]; + hrvStatusSummaryMesgs?: HrvStatusSummaryMesg[]; + hrvValueMesgs?: HrvValueMesg[]; + rawBbiMesgs?: RawBbiMesg[]; + respirationRateMesgs?: RespirationRateMesg[]; + chronoShotSessionMesgs?: ChronoShotSessionMesg[]; + chronoShotDataMesgs?: ChronoShotDataMesg[]; + tankUpdateMesgs?: TankUpdateMesg[]; + tankSummaryMesgs?: TankSummaryMesg[]; + sleepAssessmentMesgs?: SleepAssessmentMesg[]; + sleepDisruptionSeverityPeriodMesgs?: SleepDisruptionSeverityPeriodMesg[]; + sleepDisruptionOvernightSeverityMesgs?: SleepDisruptionOvernightSeverityMesg[]; + napEventMesgs?: NapEventMesg[]; + skinTempOvernightMesgs?: SkinTempOvernightMesg[]; + padMesgs?: PadMesg[]; +} diff --git a/src/types/profile.d.ts b/src/types/profile.d.ts new file mode 100644 index 0000000..5177008 --- /dev/null +++ b/src/types/profile.d.ts @@ -0,0 +1,96 @@ +///////////////////////////////////////////////////////////////////////////////////////////// +// Copyright 2026 Garmin International, Inc. +// Licensed under the Flexible and Interoperable Data Transfer (FIT) Protocol License; you +// may not use this file except in compliance with the Flexible and Interoperable Data +// Transfer (FIT) Protocol License. +///////////////////////////////////////////////////////////////////////////////////////////// +// ****WARNING**** This file is auto-generated! Do NOT edit this file. +// Profile Version = 21.205.0Release +// Tag = production/release/21.205.0-0-gb3c261eb +///////////////////////////////////////////////////////////////////////////////////////////// + + +export type FitBaseTypeString = + | 'enum' + | 'byte' + | 'sint8' + | 'uint8' + | 'uint8z' + | 'sint16' + | 'uint16' + | 'uint16z' + | 'sint32' + | 'uint32' + | 'uint32z' + | 'sint64' + | 'uint64' + | 'uint64z' + | 'float64' + | 'float32' + | 'string'; + +/** A reference mapping a subfield's activation value to its controlling field name */ +export interface SubFieldMap { + name: string; + value: number; +} + +/** A subfield that is active when a controlling field matches a specific value */ +export interface ProfileSubField { + name: string; + type: string; + baseType: FitBaseTypeString; + scale: number | number[]; + offset: number | number[]; + units: string | string[]; + bits: number[]; + components: string[]; + hasComponents: boolean; + map: SubFieldMap[]; +} + +/** A field definition within a profile message */ +export interface ProfileField { + num: number; + name: string; + type: string; + baseType: FitBaseTypeString; + array: boolean; + scale: number | number[]; + offset: number | number[]; + units: string | string[]; + bits: number[]; + components: string[]; + isAccumulated: boolean; + hasComponents: boolean; + subFields: ProfileSubField[]; +} + +/** A message definition from the FIT profile */ +export interface ProfileMesg { + num: number; + name: string; + messagesKey: string; + fields: Record; +} + +export declare const Profile: { + /** The FIT profile version */ + readonly version: { + major: number; + minor: number; + patch: number; + type: string; + }; + readonly CommonFields: { + readonly PartIndex: number; + readonly Timestamp: number; + readonly MessageIndex: number; + }; + /** All profile messages keyed by message number */ + readonly messages: Record; + /** All profile types keyed by type name, then value */ + readonly types: Record>; + /** Message number constants keyed by message name */ + readonly MesgNum: Record; +}; diff --git a/src/types/stream.d.ts b/src/types/stream.d.ts new file mode 100644 index 0000000..5bad0e2 --- /dev/null +++ b/src/types/stream.d.ts @@ -0,0 +1,118 @@ +///////////////////////////////////////////////////////////////////////////////////////////// +// Copyright 2026 Garmin International, Inc. +// Licensed under the Flexible and Interoperable Data Transfer (FIT) Protocol License; you +// may not use this file except in compliance with the Flexible and Interoperable Data +// Transfer (FIT) Protocol License. +///////////////////////////////////////////////////////////////////////////////////////////// +// ****WARNING**** This file is auto-generated! Do NOT edit this file. +// Profile Version = 21.205.0Release +// Tag = production/release/21.205.0-0-gb3c261eb +///////////////////////////////////////////////////////////////////////////////////////////// + + +import { CrcCalculator } from "./crc-calculator"; +import { FieldValue } from "./mesg"; + +/** Represents a FIT file's bytes and provides sequential read access. */ +export class Stream { + /** + * Creates a Stream containing a FIT file. + * @param buffer - `ArrayBuffer` containing the FIT file bytes. + */ + constructor(buffer: ArrayBuffer); + + /** + * Convenience method for creating a Stream from a byte array. + * @param data - An array of bytes or a `Uint8Array`. + * @returns A new Stream object. + */ + static fromByteArray(data: number[] | Uint8Array): Stream; + + /** + * Convenience method for creating a Stream from a Node Buffer. + * @param buffer - Node `Buffer` of bytes. + * @returns A new Stream object. + */ + static fromBuffer(buffer: Uint8Array): Stream; + + /** + * Convenience method for creating a Stream from an `ArrayBuffer`. + * @param buffer - An `ArrayBuffer` of bytes. + * @returns A new Stream object. + */ + static fromArrayBuffer(buffer: ArrayBuffer): Stream; + + /** Total length of the underlying buffer in bytes. */ + readonly length: number; + /** Number of bytes read. */ + readonly bytesRead: number; + /** Current read position. */ + readonly position: number; + /** CRC calculator, updated as bytes are read. */ + crcCalculator: CrcCalculator; + + /** Resets the read position to 0. */ + reset(): void; + /** + * Seeks to an absolute byte offset. + * @param position - Byte offset to seek to. + */ + seek(position: number): void; + /** + * Returns a copy of the underlying buffer for the given range. + * @param begin - Start index (inclusive). + * @param end - End index (exclusive). + */ + slice(begin: number, end: number): ArrayBuffer; + /** Returns the byte at the current position without advancing. */ + peekByte(): number; + /** Reads and returns one byte. */ + readByte(): number; + /** + * Reads `size` bytes and returns them as a `Uint8Array`. + * @param size - Number of bytes to read. + */ + readBytes(size: number): Uint8Array; + + /** + * Reads a value of the given FIT base type from the stream. + * @param baseType - FIT base type constant. + * @param size - Number of bytes to read. + * @param opts - Read options. + */ + readValue(baseType: number, size: number, opts?: ReadValueOptions): FieldValue; + + /** Reads an unsigned 8-bit integer. */ + readUInt8(opts?: ReadValueOptions): number; + /** Reads a signed 8-bit integer. */ + readInt8(opts?: ReadValueOptions): number; + /** Reads an unsigned 16-bit integer. */ + readUInt16(opts?: ReadValueOptions): number; + /** Reads a signed 16-bit integer. */ + readInt16(opts?: ReadValueOptions): number; + /** Reads an unsigned 32-bit integer. */ + readUInt32(opts?: ReadValueOptions): number; + /** Reads a signed 32-bit integer. */ + readInt32(opts?: ReadValueOptions): number; + /** Reads an unsigned 64-bit integer as a `bigint`. */ + readUInt64(opts?: ReadValueOptions): bigint; + /** Reads a signed 64-bit integer as a `bigint`. */ + readInt64(opts?: ReadValueOptions): bigint; + /** Reads a 32-bit IEEE 754 float. */ + readFloat32(opts?: ReadValueOptions): number; + /** Reads a 64-bit IEEE 754 float. */ + readFloat64(opts?: ReadValueOptions): number; + /** + * Reads a null-terminated UTF-8 string of up to `strlen` bytes. + * @param strlen - Maximum number of bytes to read. + */ + readString(strlen: number): string; +} + +/** Options shared by the typed read methods on {@link Stream}. */ +export type ReadValueOptions = { + /** Byte order for this read. Defaults to `true`. */ + littleEndian?: boolean; + /** When `true`, invalid values are returned as `null`. Defaults to `true`. */ + convertInvalidToNull?: boolean; +} diff --git a/src/types/types.d.ts b/src/types/types.d.ts new file mode 100644 index 0000000..14b8e0d --- /dev/null +++ b/src/types/types.d.ts @@ -0,0 +1,4871 @@ +///////////////////////////////////////////////////////////////////////////////////////////// +// Copyright 2026 Garmin International, Inc. +// Licensed under the Flexible and Interoperable Data Transfer (FIT) Protocol License; you +// may not use this file except in compliance with the Flexible and Interoperable Data +// Transfer (FIT) Protocol License. +///////////////////////////////////////////////////////////////////////////////////////////// +// ****WARNING**** This file is auto-generated! Do NOT edit this file. +// Profile Version = 21.205.0Release +// Tag = production/release/21.205.0-0-gb3c261eb +///////////////////////////////////////////////////////////////////////////////////////////// + + +declare namespace Types { + + export type Enum = number; + + export type Sint8 = number; + + export type Uint8 = number; + + export type Sint16 = number; + + export type Uint16 = number; + + export type Sint32 = number; + + export type Uint32 = number; + + export type String = string; + + export type Float32 = number; + + export type Float64 = number; + + export type Uint8z = number; + + export type Uint16z = number; + + export type Uint32z = number; + + export type Byte = number; + + export type Sint64 = bigint; + + export type Uint64 = bigint; + + export type Uint64z = bigint; + + export type Bool = 0 | 1; + + export type File = number + | "device" /** Read only, single file. Must be in root directory. */ + | "settings" /** Read/write, single file. Directory=Settings */ + | "sport" /** Read/write, multiple files, file number = sport type. Directory=Sports */ + | "activity" /** Read/erase, multiple files. Directory=Activities */ + | "workout" /** Read/write/erase, multiple files. Directory=Workouts */ + | "course" /** Read/write/erase, multiple files. Directory=Courses */ + | "schedules" /** Read/write, single file. Directory=Schedules */ + | "weight" /** Read only, single file. Circular buffer. All message definitions at start of file. Directory=Weight */ + | "totals" /** Read only, single file. Directory=Totals */ + | "goals" /** Read/write, single file. Directory=Goals */ + | "bloodPressure" /** Read only. Directory=Blood Pressure */ + | "monitoringA" /** Read only. Directory=Monitoring. File number=sub type. */ + | "activitySummary" /** Read/erase, multiple files. Directory=Activities */ + | "monitoringDaily" + | "monitoringB" /** Read only. Directory=Monitoring. File number=identifier */ + | "segment" /** Read/write/erase. Multiple Files. Directory=Segments */ + | "segmentList" /** Read/write/erase. Single File. Directory=Segments */ + | "exdConfiguration" /** Read/write/erase. Single File. Directory=Settings */ + | "mfgRangeMin" /** 0xF7 - 0xFE reserved for manufacturer specific file types */ + | "mfgRangeMax" /** 0xF7 - 0xFE reserved for manufacturer specific file types */; + + export type MesgNum = number + | "fileId" + | "capabilities" + | "deviceSettings" + | "userProfile" + | "hrmProfile" + | "sdmProfile" + | "bikeProfile" + | "zonesTarget" + | "hrZone" + | "powerZone" + | "metZone" + | "sport" + | "trainingSettings" + | "goal" + | "session" + | "lap" + | "record" + | "event" + | "deviceInfo" + | "workout" + | "workoutStep" + | "schedule" + | "weightScale" + | "course" + | "coursePoint" + | "totals" + | "activity" + | "software" + | "fileCapabilities" + | "mesgCapabilities" + | "fieldCapabilities" + | "fileCreator" + | "bloodPressure" + | "speedZone" + | "monitoring" + | "trainingFile" + | "hrv" + | "antRx" + | "antTx" + | "antChannelId" + | "length" + | "monitoringInfo" + | "pad" + | "slaveDevice" + | "connectivity" + | "weatherConditions" + | "weatherAlert" + | "cadenceZone" + | "hr" + | "segmentLap" + | "memoGlob" + | "segmentId" + | "segmentLeaderboardEntry" + | "segmentPoint" + | "segmentFile" + | "workoutSession" + | "watchfaceSettings" + | "gpsMetadata" + | "cameraEvent" + | "timestampCorrelation" + | "gyroscopeData" + | "accelerometerData" + | "threeDSensorCalibration" + | "videoFrame" + | "obdiiData" + | "nmeaSentence" + | "aviationAttitude" + | "video" + | "videoTitle" + | "videoDescription" + | "videoClip" + | "ohrSettings" + | "exdScreenConfiguration" + | "exdDataFieldConfiguration" + | "exdDataConceptConfiguration" + | "fieldDescription" + | "developerDataId" + | "magnetometerData" + | "barometerData" + | "oneDSensorCalibration" + | "monitoringHrData" + | "timeInZone" + | "set" + | "stressLevel" + | "maxMetData" + | "diveSettings" + | "diveGas" + | "diveAlarm" + | "exerciseTitle" + | "diveSummary" + | "spo2Data" + | "sleepLevel" + | "jump" + | "aadAccelFeatures" + | "beatIntervals" + | "respirationRate" + | "hsaAccelerometerData" + | "hsaStepData" + | "hsaSpo2Data" + | "hsaStressData" + | "hsaRespirationData" + | "hsaHeartRateData" + | "split" + | "splitSummary" + | "hsaBodyBatteryData" + | "hsaEvent" + | "climbPro" + | "tankUpdate" + | "tankSummary" + | "sleepAssessment" + | "hrvStatusSummary" + | "hrvValue" + | "rawBbi" + | "deviceAuxBatteryInfo" + | "hsaGyroscopeData" + | "chronoShotSession" + | "chronoShotData" + | "hsaConfigurationData" + | "diveApneaAlarm" + | "skinTempOvernight" + | "hsaWristTemperatureData" /** Message number for the HSA wrist temperature data message */ + | "napEvent" + | "sleepDisruptionSeverityPeriod" + | "sleepDisruptionOvernightSeverity" + | "mfgRangeMin" /** 0xFF00 - 0xFFFE reserved for manufacturer specific messages */ + | "mfgRangeMax" /** 0xFF00 - 0xFFFE reserved for manufacturer specific messages */; + + export type Checksum = number + | "clear" /** Allows clear of checksum for flash memory where can only write 1 to 0 without erasing sector. */ + | "ok" /** Set to mark checksum as valid if computes to invalid values 0 or 0xFF. Checksum can also be set to ok to save encoding computation time. */; + + export type FileFlags = number + | "read" + | "write" + | "erase"; + + export type MesgCount = number + | "numPerFile" + | "maxPerFile" + | "maxPerFileType"; + + /** seconds since UTC 00:00 Dec 31 1989 */ + export type DateTime = number | Date + | "min" /** if date_time is < 0x10000000 then it is system time (seconds from device power on) */; + + /** seconds since 00:00 Dec 31 1989 in local time zone */ + export type LocalDateTime = number | Date + | "min" /** if date_time is < 0x10000000 then it is system time (seconds from device power on) */; + + export type MessageIndex = number + | "selected" /** message is selected if set */ + | "reserved" /** reserved (default 0) */ + | "mask" /** index */; + + export type DeviceIndex = number + | "creator" /** Creator of the file is always device index 0. */; + + export type Gender = number + | "female" + | "male"; + + export type Language = number + | "english" + | "french" + | "italian" + | "german" + | "spanish" + | "croatian" + | "czech" + | "danish" + | "dutch" + | "finnish" + | "greek" + | "hungarian" + | "norwegian" + | "polish" + | "portuguese" + | "slovakian" + | "slovenian" + | "swedish" + | "russian" + | "turkish" + | "latvian" + | "ukrainian" + | "arabic" + | "farsi" + | "bulgarian" + | "romanian" + | "chinese" + | "japanese" + | "korean" + | "taiwanese" + | "thai" + | "hebrew" + | "brazilianPortuguese" + | "indonesian" + | "malaysian" + | "vietnamese" + | "burmese" + | "mongolian" + | "custom"; + + /** Bit field corresponding to language enum type (1 << language). */ + export type LanguageBits0 = number + | "english" + | "french" + | "italian" + | "german" + | "spanish" + | "croatian" + | "czech" + | "danish"; + + export type LanguageBits1 = number + | "dutch" + | "finnish" + | "greek" + | "hungarian" + | "norwegian" + | "polish" + | "portuguese" + | "slovakian"; + + export type LanguageBits2 = number + | "slovenian" + | "swedish" + | "russian" + | "turkish" + | "latvian" + | "ukrainian" + | "arabic" + | "farsi"; + + export type LanguageBits3 = number + | "bulgarian" + | "romanian" + | "chinese" + | "japanese" + | "korean" + | "taiwanese" + | "thai" + | "hebrew"; + + export type LanguageBits4 = number + | "brazilianPortuguese" + | "indonesian" + | "malaysian" + | "vietnamese" + | "burmese" + | "mongolian"; + + export type TimeZone = number + | "almaty" + | "bangkok" + | "bombay" + | "brasilia" + | "cairo" + | "capeVerdeIs" + | "darwin" + | "eniwetok" + | "fiji" + | "hongKong" + | "islamabad" + | "kabul" + | "magadan" + | "midAtlantic" + | "moscow" + | "muscat" + | "newfoundland" + | "samoa" + | "sydney" + | "tehran" + | "tokyo" + | "usAlaska" + | "usAtlantic" + | "usCentral" + | "usEastern" + | "usHawaii" + | "usMountain" + | "usPacific" + | "other" + | "auckland" + | "kathmandu" + | "europeWesternWet" + | "europeCentralCet" + | "europeEasternEet" + | "jakarta" + | "perth" + | "adelaide" + | "brisbane" + | "tasmania" + | "iceland" + | "amsterdam" + | "athens" + | "barcelona" + | "berlin" + | "brussels" + | "budapest" + | "copenhagen" + | "dublin" + | "helsinki" + | "lisbon" + | "london" + | "madrid" + | "munich" + | "oslo" + | "paris" + | "prague" + | "reykjavik" + | "rome" + | "stockholm" + | "vienna" + | "warsaw" + | "zurich" + | "quebec" + | "ontario" + | "manitoba" + | "saskatchewan" + | "alberta" + | "britishColumbia" + | "boise" + | "boston" + | "chicago" + | "dallas" + | "denver" + | "kansasCity" + | "lasVegas" + | "losAngeles" + | "miami" + | "minneapolis" + | "newYork" + | "newOrleans" + | "phoenix" + | "santaFe" + | "seattle" + | "washingtonDc" + | "usArizona" + | "chita" + | "ekaterinburg" + | "irkutsk" + | "kaliningrad" + | "krasnoyarsk" + | "novosibirsk" + | "petropavlovskKamchatskiy" + | "samara" + | "vladivostok" + | "mexicoCentral" + | "mexicoMountain" + | "mexicoPacific" + | "capeTown" + | "winkhoek" + | "lagos" + | "riyahd" + | "venezuela" + | "australiaLh" + | "santiago" + | "manual" + | "automatic"; + + export type DisplayMeasure = number + | "metric" + | "statute" + | "nautical"; + + export type DisplayHeart = number + | "bpm" + | "max" + | "reserve"; + + export type DisplayPower = number + | "watts" + | "percentFtp"; + + export type DisplayPosition = number + | "degree" /** dd.dddddd */ + | "degreeMinute" /** dddmm.mmm */ + | "degreeMinuteSecond" /** dddmmss */ + | "austrianGrid" /** Austrian Grid (BMN) */ + | "britishGrid" /** British National Grid */ + | "dutchGrid" /** Dutch grid system */ + | "hungarianGrid" /** Hungarian grid system */ + | "finnishGrid" /** Finnish grid system Zone3 KKJ27 */ + | "germanGrid" /** Gausss Krueger (German) */ + | "icelandicGrid" /** Icelandic Grid */ + | "indonesianEquatorial" /** Indonesian Equatorial LCO */ + | "indonesianIrian" /** Indonesian Irian LCO */ + | "indonesianSouthern" /** Indonesian Southern LCO */ + | "indiaZone0" /** India zone 0 */ + | "indiaZoneIA" /** India zone IA */ + | "indiaZoneIB" /** India zone IB */ + | "indiaZoneIIA" /** India zone IIA */ + | "indiaZoneIIB" /** India zone IIB */ + | "indiaZoneIIIA" /** India zone IIIA */ + | "indiaZoneIIIB" /** India zone IIIB */ + | "indiaZoneIVA" /** India zone IVA */ + | "indiaZoneIVB" /** India zone IVB */ + | "irishTransverse" /** Irish Transverse Mercator */ + | "irishGrid" /** Irish Grid */ + | "loran" /** Loran TD */ + | "maidenheadGrid" /** Maidenhead grid system */ + | "mgrsGrid" /** MGRS grid system */ + | "newZealandGrid" /** New Zealand grid system */ + | "newZealandTransverse" /** New Zealand Transverse Mercator */ + | "qatarGrid" /** Qatar National Grid */ + | "modifiedSwedishGrid" /** Modified RT-90 (Sweden) */ + | "swedishGrid" /** RT-90 (Sweden) */ + | "southAfricanGrid" /** South African Grid */ + | "swissGrid" /** Swiss CH-1903 grid */ + | "taiwanGrid" /** Taiwan Grid */ + | "unitedStatesGrid" /** United States National Grid */ + | "utmUpsGrid" /** UTM/UPS grid system */ + | "westMalayan" /** West Malayan RSO */ + | "borneoRso" /** Borneo RSO */ + | "estonianGrid" /** Estonian grid system */ + | "latvianGrid" /** Latvian Transverse Mercator */ + | "swedishRef99Grid" /** Reference Grid 99 TM (Swedish) */; + + export type Switch = number + | "off" + | "on" + | "auto"; + + export type Sport = number + | "generic" + | "running" + | "cycling" + | "transition" /** Mulitsport transition */ + | "fitnessEquipment" + | "swimming" + | "basketball" + | "soccer" + | "tennis" + | "americanFootball" + | "training" + | "walking" + | "crossCountrySkiing" + | "alpineSkiing" + | "snowboarding" + | "rowing" + | "mountaineering" + | "hiking" + | "multisport" + | "paddling" + | "flying" + | "eBiking" + | "motorcycling" + | "boating" + | "driving" + | "golf" + | "hangGliding" + | "horsebackRiding" + | "hunting" + | "fishing" + | "inlineSkating" + | "rockClimbing" + | "sailing" + | "iceSkating" + | "skyDiving" + | "snowshoeing" + | "snowmobiling" + | "standUpPaddleboarding" + | "surfing" + | "wakeboarding" + | "waterSkiing" + | "kayaking" + | "rafting" + | "windsurfing" + | "kitesurfing" + | "tactical" + | "jumpmaster" + | "boxing" + | "floorClimbing" + | "baseball" + | "diving" + | "shooting" /** Sport Shooting bits, set here for sport_bits alignment */ + | "winterSport" + | "grinding" /** Sailing position, operating manual winches to power boat controls */ + | "hiit" + | "videoGaming" + | "racket" + | "wheelchairPushWalk" + | "wheelchairPushRun" + | "meditation" + | "paraSport" + | "discGolf" + | "teamSport" + | "cricket" + | "rugby" + | "hockey" + | "lacrosse" + | "volleyball" + | "waterTubing" + | "wakesurfing" + | "waterSport" + | "archery" + | "mixedMartialArts" + | "motorSports" + | "snorkeling" + | "dance" + | "jumpRope" + | "poolApnea" + | "mobility" + | "geocaching" + | "canoeing" + | "all" /** All is for goals only to include all sports. */; + + /** Bit field corresponding to sport enum type (1 << sport). */ + export type SportBits0 = number + | "generic" + | "running" + | "cycling" + | "transition" /** Mulitsport transition */ + | "fitnessEquipment" + | "swimming" + | "basketball" + | "soccer"; + + /** Bit field corresponding to sport enum type (1 << (sport-8)). */ + export type SportBits1 = number + | "tennis" + | "americanFootball" + | "training" + | "walking" + | "crossCountrySkiing" + | "alpineSkiing" + | "snowboarding" + | "rowing"; + + /** Bit field corresponding to sport enum type (1 << (sport-16)). */ + export type SportBits2 = number + | "mountaineering" + | "hiking" + | "multisport" + | "paddling" + | "flying" + | "eBiking" + | "motorcycling" + | "boating"; + + /** Bit field corresponding to sport enum type (1 << (sport-24)). */ + export type SportBits3 = number + | "driving" + | "golf" + | "hangGliding" + | "horsebackRiding" + | "hunting" + | "fishing" + | "inlineSkating" + | "rockClimbing"; + + /** Bit field corresponding to sport enum type (1 << (sport-32)). */ + export type SportBits4 = number + | "sailing" + | "iceSkating" + | "skyDiving" + | "snowshoeing" + | "snowmobiling" + | "standUpPaddleboarding" + | "surfing" + | "wakeboarding"; + + /** Bit field corresponding to sport enum type (1 << (sport-40)). */ + export type SportBits5 = number + | "waterSkiing" + | "kayaking" + | "rafting" + | "windsurfing" + | "kitesurfing" + | "tactical" + | "jumpmaster" + | "boxing"; + + /** Bit field corresponding to sport enum type (1 << (sport-48)). */ + export type SportBits6 = number + | "floorClimbing"; + + export type SubSport = number + | "generic" + | "treadmill" /** Run/Fitness Equipment */ + | "street" /** Run */ + | "trail" /** Run */ + | "track" /** Run */ + | "spin" /** Cycling */ + | "indoorCycling" /** Cycling/Fitness Equipment */ + | "road" /** Cycling */ + | "mountain" /** Cycling */ + | "downhill" /** Cycling */ + | "recumbent" /** Cycling */ + | "cyclocross" /** Cycling */ + | "handCycling" /** Cycling */ + | "trackCycling" /** Cycling */ + | "indoorRowing" /** Fitness Equipment */ + | "elliptical" /** Fitness Equipment */ + | "stairClimbing" /** Fitness Equipment */ + | "lapSwimming" /** Swimming */ + | "openWater" /** Swimming */ + | "flexibilityTraining" /** Training */ + | "strengthTraining" /** Training */ + | "warmUp" /** Tennis */ + | "match" /** Tennis */ + | "exercise" /** Tennis */ + | "challenge" + | "indoorSkiing" /** Fitness Equipment */ + | "cardioTraining" /** Training */ + | "indoorWalking" /** Walking/Fitness Equipment */ + | "eBikeFitness" /** E-Biking */ + | "bmx" /** Cycling */ + | "casualWalking" /** Walking */ + | "speedWalking" /** Walking */ + | "bikeToRunTransition" /** Transition */ + | "runToBikeTransition" /** Transition */ + | "swimToBikeTransition" /** Transition */ + | "atv" /** Motorcycling */ + | "motocross" /** Motorcycling */ + | "backcountry" /** Alpine Skiing/Snowboarding */ + | "resort" /** Alpine Skiing/Snowboarding */ + | "rcDrone" /** Flying */ + | "wingsuit" /** Flying */ + | "whitewater" /** Kayaking/Rafting */ + | "skateSkiing" /** Cross Country Skiing */ + | "yoga" /** Training */ + | "pilates" /** Fitness Equipment */ + | "indoorRunning" /** Run */ + | "gravelCycling" /** Cycling */ + | "eBikeMountain" /** Cycling */ + | "commuting" /** Cycling */ + | "mixedSurface" /** Cycling */ + | "navigate" + | "trackMe" + | "map" + | "singleGasDiving" /** Diving */ + | "multiGasDiving" /** Diving */ + | "gaugeDiving" /** Diving */ + | "apneaDiving" /** Diving */ + | "apneaHunting" /** Diving */ + | "virtualActivity" + | "obstacle" /** Used for events where participants run, crawl through mud, climb over walls, etc. */ + | "breathing" + | "ccrDiving" /** Diving w/ closed circuit rebreather */ + | "sailRace" /** Sailing */ + | "expedition" /** Generic */ + | "ultra" /** Ultramarathon */ + | "indoorClimbing" /** Climbing */ + | "bouldering" /** Climbing */ + | "hiit" /** High Intensity Interval Training */ + | "indoorGrinding" /** Sailing position, operating manual winches to power boat controls */ + | "huntingWithDogs" /** Hunting */ + | "amrap" /** HIIT */ + | "emom" /** HIIT */ + | "tabata" /** HIIT */ + | "esport" /** Video Gaming, Cycling, etc. */ + | "triathlon" /** Multisport */ + | "duathlon" /** Multisport */ + | "brick" /** Multisport */ + | "swimRun" /** Multisport */ + | "adventureRace" /** Multisport */ + | "truckerWorkout" /** DEZL trucker workout training sport */ + | "pickleball" /** Racket */ + | "padel" /** Racket */ + | "indoorWheelchairWalk" + | "indoorWheelchairRun" + | "indoorHandCycling" + | "field" /** Hockey */ + | "ice" /** Hockey */ + | "ultimate" /** Disc */ + | "platform" /** Racket */ + | "squash" /** Racket */ + | "badminton" /** Racket */ + | "racquetball" /** Racket */ + | "tableTennis" /** Racket */ + | "overland" + | "trollingMotor" /** Generic */ + | "flyCanopy" /** Flying */ + | "flyParaglide" /** Flying */ + | "flyParamotor" /** Flying */ + | "flyPressurized" /** Flying */ + | "flyNavigate" /** Flying */ + | "flyTimer" /** Flying */ + | "flyAltimeter" /** Flying */ + | "flyWx" /** Flying */ + | "flyVfr" /** Flying */ + | "flyIfr" /** Flying */ + | "dynamicApnea" + | "enduro" /** Cycling */ + | "rucking" /** Hiking */ + | "rally" /** Motor sports */ + | "poolTriathlon" /** Multisport */ + | "eBikeEnduro" /** Cycling */ + | "all"; + + export type SportEvent = number + | "uncategorized" + | "geocaching" + | "fitness" + | "recreation" + | "race" + | "specialEvent" + | "training" + | "transportation" + | "touring"; + + export type Activity = number + | "manual" + | "autoMultiSport"; + + export type Intensity = number + | "active" + | "rest" + | "warmup" + | "cooldown" + | "recovery" + | "interval" + | "other"; + + export type SessionTrigger = number + | "activityEnd" + | "manual" /** User changed sport. */ + | "autoMultiSport" /** Auto multi-sport feature is enabled and user pressed lap button to advance session. */ + | "fitnessEquipment" /** Auto sport change caused by user linking to fitness equipment. */; + + export type AutolapTrigger = number + | "time" + | "distance" + | "positionStart" + | "positionLap" + | "positionWaypoint" + | "positionMarked" + | "off" + | "autoSelect"; + + export type LapTrigger = number + | "manual" + | "time" + | "distance" + | "positionStart" + | "positionLap" + | "positionWaypoint" + | "positionMarked" + | "sessionEnd" + | "fitnessEquipment"; + + export type TimeMode = number + | "hour12" + | "hour24" /** Does not use a leading zero and has a colon */ + | "military" /** Uses a leading zero and does not have a colon */ + | "hour12WithSeconds" + | "hour24WithSeconds" + | "utc"; + + export type BacklightMode = number + | "off" + | "manual" + | "keyAndMessages" + | "autoBrightness" + | "smartNotifications" + | "keyAndMessagesNight" + | "keyAndMessagesAndSmartNotifications"; + + export type DateMode = number + | "dayMonth" + | "monthDay"; + + /** Timeout in seconds. */ + export type BacklightTimeout = number + | "infinite" /** Backlight stays on forever. */; + + export type Event = number + | "timer" /** Group 0. Start / stop_all */ + | "workout" /** start / stop */ + | "workoutStep" /** Start at beginning of workout. Stop at end of each step. */ + | "powerDown" /** stop_all group 0 */ + | "powerUp" /** stop_all group 0 */ + | "offCourse" /** start / stop group 0 */ + | "session" /** Stop at end of each session. */ + | "lap" /** Stop at end of each lap. */ + | "coursePoint" /** marker */ + | "battery" /** marker */ + | "virtualPartnerPace" /** Group 1. Start at beginning of activity if VP enabled, when VP pace is changed during activity or VP enabled mid activity. stop_disable when VP disabled. */ + | "hrHighAlert" /** Group 0. Start / stop when in alert condition. */ + | "hrLowAlert" /** Group 0. Start / stop when in alert condition. */ + | "speedHighAlert" /** Group 0. Start / stop when in alert condition. */ + | "speedLowAlert" /** Group 0. Start / stop when in alert condition. */ + | "cadHighAlert" /** Group 0. Start / stop when in alert condition. */ + | "cadLowAlert" /** Group 0. Start / stop when in alert condition. */ + | "powerHighAlert" /** Group 0. Start / stop when in alert condition. */ + | "powerLowAlert" /** Group 0. Start / stop when in alert condition. */ + | "recoveryHr" /** marker */ + | "batteryLow" /** marker */ + | "timeDurationAlert" /** Group 1. Start if enabled mid activity (not required at start of activity). Stop when duration is reached. stop_disable if disabled. */ + | "distanceDurationAlert" /** Group 1. Start if enabled mid activity (not required at start of activity). Stop when duration is reached. stop_disable if disabled. */ + | "calorieDurationAlert" /** Group 1. Start if enabled mid activity (not required at start of activity). Stop when duration is reached. stop_disable if disabled. */ + | "activity" /** Group 1.. Stop at end of activity. */ + | "fitnessEquipment" /** marker */ + | "length" /** Stop at end of each length. */ + | "userMarker" /** marker */ + | "sportPoint" /** marker */ + | "calibration" /** start/stop/marker */ + | "frontGearChange" /** marker */ + | "rearGearChange" /** marker */ + | "riderPositionChange" /** marker */ + | "elevHighAlert" /** Group 0. Start / stop when in alert condition. */ + | "elevLowAlert" /** Group 0. Start / stop when in alert condition. */ + | "commTimeout" /** marker */ + | "autoActivityDetect" /** marker */ + | "diveAlert" /** marker */ + | "diveGasSwitched" /** marker */ + | "tankPressureReserve" /** marker */ + | "tankPressureCritical" /** marker */ + | "tankLost" /** marker */ + | "radarThreatAlert" /** start/stop/marker */ + | "tankBatteryLow" /** marker */ + | "tankPodConnected" /** marker - tank pod has connected */ + | "tankPodDisconnected" /** marker - tank pod has lost connection */; + + export type EventType = number + | "start" + | "stop" + | "consecutiveDepreciated" + | "marker" + | "stopAll" + | "beginDepreciated" + | "endDepreciated" + | "endAllDepreciated" + | "stopDisable" + | "stopDisableAll"; + + /** timer event data */ + export type TimerTrigger = number + | "manual" + | "auto" + | "fitnessEquipment"; + + /** fitness equipment event data */ + export type FitnessEquipmentState = number + | "ready" + | "inUse" + | "paused" + | "unknown" /** lost connection to fitness equipment */; + + export type Tone = number + | "off" + | "tone" + | "vibrate" + | "toneAndVibrate"; + + export type Autoscroll = number + | "none" + | "slow" + | "medium" + | "fast"; + + export type ActivityClass = number + | "level" /** 0 to 100 */ + | "levelMax" + | "athlete"; + + export type HrZoneCalc = number + | "custom" + | "percentMaxHr" + | "percentHrr" + | "percentLthr"; + + export type PwrZoneCalc = number + | "custom" + | "percentFtp"; + + export type WktStepDuration = number + | "time" + | "distance" + | "hrLessThan" + | "hrGreaterThan" + | "calories" + | "open" + | "repeatUntilStepsCmplt" + | "repeatUntilTime" + | "repeatUntilDistance" + | "repeatUntilCalories" + | "repeatUntilHrLessThan" + | "repeatUntilHrGreaterThan" + | "repeatUntilPowerLessThan" + | "repeatUntilPowerGreaterThan" + | "powerLessThan" + | "powerGreaterThan" + | "trainingPeaksTss" + | "repeatUntilPowerLastLapLessThan" + | "repeatUntilMaxPowerLastLapLessThan" + | "power3sLessThan" + | "power10sLessThan" + | "power30sLessThan" + | "power3sGreaterThan" + | "power10sGreaterThan" + | "power30sGreaterThan" + | "powerLapLessThan" + | "powerLapGreaterThan" + | "repeatUntilTrainingPeaksTss" + | "repetitionTime" + | "reps" + | "timeOnly"; + + export type WktStepTarget = number + | "speed" + | "heartRate" + | "open" + | "cadence" + | "power" + | "grade" + | "resistance" + | "power3s" + | "power10s" + | "power30s" + | "powerLap" + | "swimStroke" + | "speedLap" + | "heartRateLap"; + + export type Goal = number + | "time" + | "distance" + | "calories" + | "frequency" + | "steps" + | "ascent" + | "activeMinutes"; + + export type GoalRecurrence = number + | "off" + | "daily" + | "weekly" + | "monthly" + | "yearly" + | "custom"; + + export type GoalSource = number + | "auto" /** Device generated */ + | "community" /** Social network sourced goal */ + | "user" /** Manually generated */; + + export type Schedule = number + | "workout" + | "course"; + + export type CoursePoint = number + | "generic" + | "summit" + | "valley" + | "water" + | "food" + | "danger" + | "left" + | "right" + | "straight" + | "firstAid" + | "fourthCategory" + | "thirdCategory" + | "secondCategory" + | "firstCategory" + | "horsCategory" + | "sprint" + | "leftFork" + | "rightFork" + | "middleFork" + | "slightLeft" + | "sharpLeft" + | "slightRight" + | "sharpRight" + | "uTurn" + | "segmentStart" + | "segmentEnd" + | "campsite" + | "aidStation" + | "restArea" + | "generalDistance" /** Used with UpAhead */ + | "service" + | "energyGel" + | "sportsDrink" + | "mileMarker" + | "checkpoint" + | "shelter" + | "meetingSpot" + | "overlook" + | "toilet" + | "shower" + | "gear" + | "sharpCurve" + | "steepIncline" + | "tunnel" + | "bridge" + | "obstacle" + | "crossing" + | "store" + | "transition" + | "navaid" + | "transport" + | "alert" + | "info"; + + export type Manufacturer = number + | "garmin" + | "garminFr405Antfs" /** Do not use. Used by FR405 for ANTFS man id. */ + | "zephyr" + | "dayton" + | "idt" + | "srm" + | "quarq" + | "ibike" + | "saris" + | "sparkHk" + | "tanita" + | "echowell" + | "dynastreamOem" + | "nautilus" + | "dynastream" + | "timex" + | "metrigear" + | "xelic" + | "beurer" + | "cardiosport" + | "aAndD" + | "hmm" + | "suunto" + | "thitaElektronik" + | "gpulse" + | "cleanMobile" + | "pedalBrain" + | "peaksware" + | "saxonar" + | "lemondFitness" + | "dexcom" + | "wahooFitness" + | "octaneFitness" + | "archinoetics" + | "theHurtBox" + | "citizenSystems" + | "magellan" + | "osynce" + | "holux" + | "concept2" + | "shimano" + | "oneGiantLeap" + | "aceSensor" + | "brimBrothers" + | "xplova" + | "perceptionDigital" + | "bf1systems" + | "pioneer" + | "spantec" + | "metalogics" + | "4iiiis" + | "seikoEpson" + | "seikoEpsonOem" + | "iforPowell" + | "maxwellGuider" + | "starTrac" + | "breakaway" + | "alatechTechnologyLtd" + | "mioTechnologyEurope" + | "rotor" + | "geonaute" + | "idBike" + | "specialized" + | "wtek" + | "physicalEnterprises" + | "northPoleEngineering" + | "bkool" + | "cateye" + | "stagesCycling" + | "sigmasport" + | "tomtom" + | "peripedal" + | "wattbike" + | "moxy" + | "ciclosport" + | "powerbahn" + | "acornProjectsAps" + | "lifebeam" + | "bontrager" + | "wellgo" + | "scosche" + | "magura" + | "woodway" + | "elite" + | "nielsenKellerman" + | "dkCity" + | "tacx" + | "directionTechnology" + | "magtonic" + | "1partcarbon" + | "insideRideTechnologies" + | "soundOfMotion" + | "stryd" + | "icg" /** Indoorcycling Group */ + | "miPulse" + | "bsxAthletics" + | "look" + | "campagnoloSrl" + | "bodyBikeSmart" + | "praxisworks" + | "limitsTechnology" /** Limits Technology Ltd. */ + | "topactionTechnology" /** TopAction Technology Inc. */ + | "cosinuss" + | "fitcare" + | "magene" + | "giantManufacturingCo" + | "tigrasport" /** Tigrasport */ + | "salutron" + | "technogym" + | "brytonSensors" + | "latitudeLimited" + | "soaringTechnology" + | "igpsport" + | "thinkrider" + | "gopherSport" + | "waterrower" + | "orangetheory" + | "inpeak" + | "kinetic" + | "johnsonHealthTech" + | "polarElectro" + | "seesense" + | "nciTechnology" + | "iqsquare" + | "leomo" + | "ifitCom" + | "corosByte" + | "versaDesign" + | "chileaf" + | "cycplus" + | "gravaaByte" + | "sigeyi" + | "coospo" + | "geoid" + | "bosch" + | "kyto" + | "kineticSports" + | "decathlonByte" + | "tqSystems" + | "tagHeuer" + | "keiserFitness" + | "zwiftByte" + | "porscheEp" + | "blackbird" + | "meilanByte" + | "ezon" + | "laisi" + | "myzone" + | "abawo" + | "bafang" + | "luhongTechnology" + | "development" + | "healthandlife" + | "lezyne" + | "scribeLabs" + | "zwift" + | "watteam" + | "recon" + | "faveroElectronics" + | "dynovelo" + | "strava" + | "precor" /** Amer Sports */ + | "bryton" + | "sram" + | "navman" /** MiTAC Global Corporation (Mio Technology) */ + | "cobi" /** COBI GmbH */ + | "spivi" + | "mioMagellan" + | "evesports" + | "sensitivusGauge" + | "podoon" + | "lifeTimeFitness" + | "falcoEMotors" /** Falco eMotors Inc. */ + | "minoura" + | "cycliq" + | "luxottica" + | "trainerRoad" + | "theSufferfest" + | "fullspeedahead" + | "virtualtraining" + | "feedbacksports" + | "omata" + | "vdo" + | "magneticdays" + | "hammerhead" + | "kineticByKurt" + | "shapelog" + | "dabuziduo" + | "jetblack" + | "coros" + | "virtugo" + | "velosense" + | "cycligentinc" + | "trailforks" + | "mahleEbikemotion" + | "nurvv" + | "microprogram" + | "zone5cloud" + | "greenteg" + | "yamahaMotors" + | "whoop" + | "gravaa" + | "onelap" + | "monarkExercise" + | "form" + | "decathlon" + | "syncros" + | "heatup" + | "cannondale" + | "trueFitness" + | "rGTCycling" + | "vasa" + | "raceRepublic" + | "fazua" + | "orekaTraining" + | "lsec" /** Lishun Electric & Communication */ + | "lululemonStudio" + | "shanyue" + | "spinningMda" + | "hilldating" + | "aeroSensor" + | "nike" + | "magicshine" + | "ictrainer" + | "absoluteCycling" + | "eoSwimbetter" + | "mywhoosh" + | "ravemen" + | "tektroRacingProducts" + | "daradInnovationCorporation" + | "cycloptim" + | "runna" + | "zepp" + | "peloton" + | "carv" + | "tissot" + | "realVelo" + | "wetech" + | "jespr" + | "huawei" + | "gotoes" + | "cadenceApp" + | "actigraphcorp"; + + export type GarminProduct = number + | "hrm1" + | "axh01" /** AXH01 HRM chipset */ + | "axb01" + | "axb02" + | "hrm2ss" + | "dsiAlf02" + | "hrm3ss" + | "hrmRunSingleByteProductId" /** hrm_run model for HRM ANT+ messaging */ + | "bsm" /** BSM model for ANT+ messaging */ + | "bcm" /** BCM model for ANT+ messaging */ + | "axs01" /** AXS01 HRM Bike Chipset model for ANT+ messaging */ + | "hrmTriSingleByteProductId" /** hrm_tri model for HRM ANT+ messaging */ + | "hrm4RunSingleByteProductId" /** hrm4 run model for HRM ANT+ messaging */ + | "fr225SingleByteProductId" /** fr225 model for HRM ANT+ messaging */ + | "gen3BsmSingleByteProductId" /** gen3_bsm model for Bike Speed ANT+ messaging */ + | "gen3BcmSingleByteProductId" /** gen3_bcm model for Bike Cadence ANT+ messaging */ + | "hrmFitSingleByteProductId" + | "oHR" /** Garmin Wearable Optical Heart Rate Sensor for ANT+ HR Profile Broadcasting */ + | "fr301China" + | "fr301Japan" + | "fr301Korea" + | "fr301Taiwan" + | "fr405" /** Forerunner 405 */ + | "fr50" /** Forerunner 50 */ + | "fr405Japan" + | "fr60" /** Forerunner 60 */ + | "dsiAlf01" + | "fr310xt" /** Forerunner 310 */ + | "edge500" + | "fr110" /** Forerunner 110 */ + | "edge800" + | "edge500Taiwan" + | "edge500Japan" + | "chirp" + | "fr110Japan" + | "edge200" + | "fr910xt" + | "edge800Taiwan" + | "edge800Japan" + | "alf04" + | "fr610" + | "fr210Japan" + | "vectorSs" + | "vectorCp" + | "edge800China" + | "edge500China" + | "approachG10" + | "fr610Japan" + | "edge500Korea" + | "fr70" + | "fr310xt4t" + | "amx" + | "fr10" + | "edge800Korea" + | "swim" + | "fr910xtChina" + | "fenix" + | "edge200Taiwan" + | "edge510" + | "edge810" + | "tempe" + | "fr910xtJapan" + | "fr620" + | "fr220" + | "fr910xtKorea" + | "fr10Japan" + | "edge810Japan" + | "virbElite" + | "edgeTouring" /** Also Edge Touring Plus */ + | "edge510Japan" + | "hrmTri" /** Also HRM-Swim */ + | "hrmRun" + | "fr920xt" + | "edge510Asia" + | "edge810China" + | "edge810Taiwan" + | "edge1000" + | "vivoFit" + | "virbRemote" + | "vivoKi" + | "fr15" + | "vivoActive" + | "edge510Korea" + | "fr620Japan" + | "fr620China" + | "fr220Japan" + | "fr220China" + | "approachS6" + | "vivoSmart" + | "fenix2" + | "epix" + | "fenix3" + | "edge1000Taiwan" + | "edge1000Japan" + | "fr15Japan" + | "edge520" + | "edge1000China" + | "fr620Russia" + | "fr220Russia" + | "vectorS" + | "edge1000Korea" + | "fr920xtTaiwan" + | "fr920xtChina" + | "fr920xtJapan" + | "virbx" + | "vivoSmartApac" + | "etrexTouch" + | "edge25" + | "fr25" + | "vivoFit2" + | "fr225" + | "fr630" + | "fr230" + | "fr735xt" + | "vivoActiveApac" + | "vector2" + | "vector2s" + | "virbxe" + | "fr620Taiwan" + | "fr220Taiwan" + | "truswing" + | "d2airvenu" + | "fenix3China" + | "fenix3Twn" + | "variaHeadlight" + | "variaTaillightOld" + | "edgeExplore1000" + | "fr225Asia" + | "variaRadarTaillight" + | "variaRadarDisplay" + | "edge20" + | "edge520Asia" + | "edge520Japan" + | "d2Bravo" + | "approachS20" + | "vivoSmart2" + | "edge1000Thai" + | "variaRemote" + | "edge25Asia" + | "edge25Jpn" + | "edge20Asia" + | "approachX40" + | "fenix3Japan" + | "vivoSmartEmea" + | "fr630Asia" + | "fr630Jpn" + | "fr230Jpn" + | "hrm4Run" + | "epixJapan" + | "vivoActiveHr" + | "vivoSmartGpsHr" + | "vivoSmartHr" + | "vivoSmartHrAsia" + | "vivoSmartGpsHrAsia" + | "vivoMove" + | "variaTaillight" + | "fr235Asia" + | "fr235Japan" + | "variaVision" + | "vivoFit3" + | "fenix3Korea" + | "fenix3Sea" + | "fenix3Hr" + | "virbUltra30" + | "indexSmartScale" + | "fr235" + | "fenix3Chronos" + | "oregon7xx" + | "rino7xx" + | "epixKorea" + | "fenix3HrChn" + | "fenix3HrTwn" + | "fenix3HrJpn" + | "fenix3HrSea" + | "fenix3HrKor" + | "nautix" + | "vivoActiveHrApac" + | "fr35" + | "oregon7xxWw" + | "edge820" + | "edgeExplore820" + | "fr735xtApac" + | "fr735xtJapan" + | "fenix5s" + | "d2BravoTitanium" + | "variaUt800" /** Varia UT 800 SW */ + | "runningDynamicsPod" + | "edge820China" + | "edge820Japan" + | "fenix5x" + | "vivoFitJr" + | "vivoSmart3" + | "vivoSport" + | "edge820Taiwan" + | "edge820Korea" + | "edge820Sea" + | "fr35Hebrew" + | "approachS60" + | "fr35Apac" + | "fr35Japan" + | "fenix3ChronosAsia" + | "virb360" + | "fr935" + | "fenix5" + | "vivoactive3" + | "fr235ChinaNfc" + | "foretrex601_701" + | "vivoMoveHr" + | "edge1030" + | "fr35Sea" + | "vector3" + | "fenix5Asia" + | "fenix5sAsia" + | "fenix5xAsia" + | "approachZ80" + | "fr35Korea" + | "d2charlie" + | "vivoSmart3Apac" + | "vivoSportApac" + | "fr935Asia" + | "descent" + | "vivoFit4" + | "fr645" + | "fr645m" + | "fr30" + | "fenix5sPlus" + | "edge130" + | "edge1030Asia" + | "vivosmart4" + | "vivoMoveHrAsia" + | "approachX10" + | "fr30Asia" + | "vivoactive3mW" + | "fr645Asia" + | "fr645mAsia" + | "edgeExplore" + | "gpsmap66" + | "approachS10" + | "vivoactive3mL" + | "fr245" + | "fr245Music" + | "approachG80" + | "edge130Asia" + | "edge1030Bontrager" + | "fenix5Plus" + | "fenix5xPlus" + | "edge520Plus" + | "fr945" + | "edge530" + | "edge830" + | "instinctEsports" + | "fenix5sPlusApac" + | "fenix5xPlusApac" + | "edge520PlusApac" + | "descentT1" + | "fr235lAsia" + | "fr245Asia" + | "vivoActive3mApac" + | "gen3Bsm" /** gen3 bike speed sensor */ + | "gen3Bcm" /** gen3 bike cadence sensor */ + | "vivoSmart4Asia" + | "vivoactive4Small" + | "vivoactive4Large" + | "venu" + | "marqDriver" + | "marqAviator" + | "marqCaptain" + | "marqCommander" + | "marqExpedition" + | "marqAthlete" + | "descentMk2" + | "fr45" + | "gpsmap66i" + | "fenix6SSport" + | "fenix6S" + | "fenix6Sport" + | "fenix6" + | "fenix6x" + | "hrmDual" /** HRM-Dual */ + | "hrmPro" /** HRM-Pro */ + | "vivoMove3Premium" + | "approachS40" + | "fr245mAsia" + | "edge530Apac" + | "edge830Apac" + | "vivoMove3" + | "vivoActive4SmallAsia" + | "vivoActive4LargeAsia" + | "vivoActive4OledAsia" + | "swim2" + | "marqDriverAsia" + | "marqAviatorAsia" + | "vivoMove3Asia" + | "fr945Asia" + | "vivoActive3tChn" + | "marqCaptainAsia" + | "marqCommanderAsia" + | "marqExpeditionAsia" + | "marqAthleteAsia" + | "indexSmartScale2" + | "instinctSolar" + | "fr45Asia" + | "vivoactive3Daimler" + | "legacyRey" + | "legacyDarthVader" + | "legacyCaptainMarvel" + | "legacyFirstAvenger" + | "fenix6sSportAsia" + | "fenix6sAsia" + | "fenix6SportAsia" + | "fenix6Asia" + | "fenix6xAsia" + | "legacyCaptainMarvelAsia" + | "legacyFirstAvengerAsia" + | "legacyReyAsia" + | "legacyDarthVaderAsia" + | "descentMk2s" + | "edge130Plus" + | "edge1030Plus" + | "rally200" /** Rally 100/200 Power Meter Series */ + | "fr745" + | "venusqMusic" + | "venusqMusicV2" + | "venusq" + | "lily" + | "marqAdventurer" + | "enduro" + | "swim2Apac" + | "marqAdventurerAsia" + | "fr945Lte" + | "descentMk2Asia" /** Mk2 and Mk2i */ + | "venu2" + | "venu2s" + | "venuDaimlerAsia" + | "marqGolfer" + | "venuDaimler" + | "fr745Asia" + | "variaRct715" + | "lilyAsia" + | "edge1030PlusAsia" + | "edge130PlusAsia" + | "approachS12" + | "enduroAsia" + | "venusqAsia" + | "edge1040" + | "marqGolferAsia" + | "venu2Plus" + | "gnss" /** Airoha AG3335M Family */ + | "fr55" + | "instinct2" + | "instinct2s" + | "fenix7s" + | "fenix7" + | "fenix7x" + | "fenix7sApac" + | "fenix7Apac" + | "fenix7xApac" + | "approachG12" + | "descentMk2sAsia" + | "approachS42" + | "epixGen2" + | "epixGen2Apac" + | "venu2sAsia" + | "venu2Asia" + | "fr945LteAsia" + | "vivoMoveSport" + | "vivomoveTrend" + | "approachS12Asia" + | "fr255Music" + | "fr255SmallMusic" + | "fr255" + | "fr255Small" + | "approachG12Asia" + | "approachS42Asia" + | "descentG1" + | "venu2PlusAsia" + | "fr955" + | "fr55Asia" + | "edge540" + | "edge840" + | "vivosmart5" + | "instinct2Asia" + | "marqGen2" /** Adventurer, Athlete, Captain, Golfer */ + | "venusq2" + | "venusq2music" + | "marqGen2Aviator" + | "d2AirX10" + | "hrmProPlus" + | "descentG1Asia" + | "tactix7" + | "instinctCrossover" + | "edgeExplore2" + | "descentMk3" + | "descentMk3i" + | "approachS70" + | "fr265Large" + | "fr265Small" + | "venu3" + | "venu3s" + | "tacxNeoSmart" /** Neo Smart, Tacx */ + | "tacxNeo2Smart" /** Neo 2 Smart, Tacx */ + | "tacxNeo2TSmart" /** Neo 2T Smart, Tacx */ + | "tacxNeoSmartBike" /** Neo Smart Bike, Tacx */ + | "tacxSatoriSmart" /** Satori Smart, Tacx */ + | "tacxFlowSmart" /** Flow Smart, Tacx */ + | "tacxVortexSmart" /** Vortex Smart, Tacx */ + | "tacxBushidoSmart" /** Bushido Smart, Tacx */ + | "tacxGeniusSmart" /** Genius Smart, Tacx */ + | "tacxFluxFluxSSmart" /** Flux/Flux S Smart, Tacx */ + | "tacxFlux2Smart" /** Flux 2 Smart, Tacx */ + | "tacxMagnum" /** Magnum, Tacx */ + | "edge1040Asia" + | "epixGen2Pro42" + | "epixGen2Pro47" + | "epixGen2Pro51" + | "fr965" + | "enduro2" + | "fenix7sProSolar" + | "fenix7ProSolar" + | "fenix7xProSolar" + | "lily2" + | "instinct2x" + | "vivoactive5" + | "fr165" + | "fr165Music" + | "edge1050" + | "descentT2" + | "hrmFit" + | "marqGen2Commander" + | "lilyAthlete" /** aka the Lily 2 Active */ + | "rallyX10" /** Rally 110/210 */ + | "fenix8Solar" + | "fenix8SolarLarge" + | "fenix8Small" + | "fenix8" + | "d2Mach1Pro" + | "enduro3" + | "instinctE40mm" + | "instinctE45mm" + | "instinct3Solar45mm" + | "instinct3Amoled45mm" + | "instinct3Amoled50mm" + | "descentG2" + | "venuX1" + | "hrm200" + | "vivoactive6" + | "fenix8Pro" + | "edge550" + | "edge850" + | "venu4" + | "venu4s" + | "approachS44" + | "edgeMtb" + | "approachS50" + | "fenixE" + | "bounce2" + | "instinct3Solar50mm" + | "tactix8Amoled" + | "tactix8Solar" + | "fr170Music" + | "fr170" + | "approachJ1" + | "d2Mach2" + | "fr702026" + | "instinctCrossoverAmoled" + | "d2AirX15" + | "d2Mach2Pro" + | "sdm4" /** SDM4 footpod */ + | "edgeRemote" + | "tacxTrainingAppWin" + | "tacxTrainingAppMac" + | "tacxTrainingAppMacCatalyst" + | "trainingCenter" + | "tacxTrainingAppAndroid" + | "tacxTrainingAppIos" + | "tacxTrainingAppLegacy" + | "connectiqSimulator" + | "androidAntplusPlugin" + | "connect" /** Garmin Connect website */; + + export type AntplusDeviceType = number + | "antfs" + | "bikePower" + | "environmentSensorLegacy" + | "multiSportSpeedDistance" + | "control" + | "fitnessEquipment" + | "bloodPressure" + | "geocacheNode" + | "lightElectricVehicle" + | "envSensor" + | "racquet" + | "controlHub" + | "muscleOxygen" + | "shifting" + | "bikeLightMain" + | "bikeLightShared" + | "exd" + | "bikeRadar" + | "bikeAero" + | "weightScale" + | "heartRate" + | "bikeSpeedCadence" + | "bikeCadence" + | "bikeSpeed" + | "strideSpeedDistance"; + + export type AntNetwork = number + | "public" + | "antplus" + | "antfs" + | "private"; + + export type WorkoutCapabilities = number + | "interval" + | "custom" + | "fitnessEquipment" + | "firstbeat" + | "newLeaf" + | "tcx" /** For backwards compatibility. Watch should add missing id fields then clear flag. */ + | "speed" /** Speed source required for workout step. */ + | "heartRate" /** Heart rate source required for workout step. */ + | "distance" /** Distance source required for workout step. */ + | "cadence" /** Cadence source required for workout step. */ + | "power" /** Power source required for workout step. */ + | "grade" /** Grade source required for workout step. */ + | "resistance" /** Resistance source required for workout step. */ + | "protected"; + + export type BatteryStatus = number + | "new" + | "good" + | "ok" + | "low" + | "critical" + | "charging" + | "unknown"; + + export type HrType = number + | "normal" + | "irregular"; + + export type CourseCapabilities = number + | "processed" + | "valid" + | "time" + | "distance" + | "position" + | "heartRate" + | "power" + | "cadence" + | "training" + | "navigation" + | "bikeway" + | "aviation" /** Denote course files to be used as flight plans */; + + export type Weight = number + | "calculating"; + + /** 0 - 100 indicates% of max hr; >100 indicates bpm (255 max) plus 100 */ + export type WorkoutHr = number + | "bpmOffset"; + + /** 0 - 1000 indicates % of functional threshold power; >1000 indicates watts plus 1000. */ + export type WorkoutPower = number + | "wattsOffset"; + + export type BpStatus = number + | "noError" + | "errorIncompleteData" + | "errorNoMeasurement" + | "errorDataOutOfRange" + | "errorIrregularHeartRate"; + + export type UserLocalId = number + | "localMin" + | "localMax" + | "stationaryMin" + | "stationaryMax" + | "portableMin" + | "portableMax"; + + export type SwimStroke = number + | "freestyle" + | "backstroke" + | "breaststroke" + | "butterfly" + | "drill" + | "mixed" + | "im" /** IM is a mixed interval containing the same number of lengths for each of: Butterfly, Backstroke, Breaststroke, Freestyle, swam in that order. */ + | "imByRound" /** For repeated workout steps, a new individual medly stroke is used for each round. */ + | "rimo" /** Reverse IM Order */; + + export type ActivityType = number + | "generic" + | "running" + | "cycling" + | "transition" /** Mulitsport transition */ + | "fitnessEquipment" + | "swimming" + | "walking" + | "sedentary" + | "all" /** All is for goals only to include all sports. */; + + export type ActivitySubtype = number + | "generic" + | "treadmill" /** Run */ + | "street" /** Run */ + | "trail" /** Run */ + | "track" /** Run */ + | "spin" /** Cycling */ + | "indoorCycling" /** Cycling */ + | "road" /** Cycling */ + | "mountain" /** Cycling */ + | "downhill" /** Cycling */ + | "recumbent" /** Cycling */ + | "cyclocross" /** Cycling */ + | "handCycling" /** Cycling */ + | "trackCycling" /** Cycling */ + | "indoorRowing" /** Fitness Equipment */ + | "elliptical" /** Fitness Equipment */ + | "stairClimbing" /** Fitness Equipment */ + | "lapSwimming" /** Swimming */ + | "openWater" /** Swimming */ + | "all"; + + export type ActivityLevel = number + | "low" + | "medium" + | "high"; + + export type Side = number + | "right" + | "left"; + + export type LeftRightBalance = number + | "mask" /** % contribution */ + | "right" /** data corresponds to right if set, otherwise unknown */; + + export type LeftRightBalance100 = number + | "mask" /** % contribution scaled by 100 */ + | "right" /** data corresponds to right if set, otherwise unknown */; + + export type LengthType = number + | "idle" /** Rest period. Length with no strokes */ + | "active" /** Length with strokes. */; + + export type DayOfWeek = number + | "sunday" + | "monday" + | "tuesday" + | "wednesday" + | "thursday" + | "friday" + | "saturday"; + + export type ConnectivityCapabilities = number + | "bluetooth" + | "bluetoothLe" + | "ant" + | "activityUpload" + | "courseDownload" + | "workoutDownload" + | "liveTrack" + | "weatherConditions" + | "weatherAlerts" + | "gpsEphemerisDownload" + | "explicitArchive" + | "setupIncomplete" + | "continueSyncAfterSoftwareUpdate" + | "connectIqAppDownload" + | "golfCourseDownload" + | "deviceInitiatesSync" /** Indicates device is in control of initiating all syncs */ + | "connectIqWatchAppDownload" + | "connectIqWidgetDownload" + | "connectIqWatchFaceDownload" + | "connectIqDataFieldDownload" + | "connectIqAppManagment" /** Device supports delete and reorder of apps via GCM */ + | "swingSensor" + | "swingSensorRemote" + | "incidentDetection" /** Device supports incident detection */ + | "audioPrompts" + | "wifiVerification" /** Device supports reporting wifi verification via GCM */ + | "trueUp" /** Device supports True Up */ + | "findMyWatch" /** Device supports Find My Watch */ + | "remoteManualSync" + | "liveTrackAutoStart" /** Device supports LiveTrack auto start */ + | "liveTrackMessaging" /** Device supports LiveTrack Messaging */ + | "instantInput" /** Device supports instant input feature */; + + export type WeatherReport = number + | "current" + | "forecast" /** Deprecated use hourly_forecast instead */ + | "hourlyForecast" + | "dailyForecast"; + + export type WeatherStatus = number + | "clear" + | "partlyCloudy" + | "mostlyCloudy" + | "rain" + | "snow" + | "windy" + | "thunderstorms" + | "wintryMix" + | "fog" + | "hazy" + | "hail" + | "scatteredShowers" + | "scatteredThunderstorms" + | "unknownPrecipitation" + | "lightRain" + | "heavyRain" + | "lightSnow" + | "heavySnow" + | "lightRainSnow" + | "heavyRainSnow" + | "cloudy"; + + export type WeatherSeverity = number + | "unknown" + | "warning" + | "watch" + | "advisory" + | "statement"; + + export type WeatherSevereType = number + | "unspecified" + | "tornado" + | "tsunami" + | "hurricane" + | "extremeWind" + | "typhoon" + | "inlandHurricane" + | "hurricaneForceWind" + | "waterspout" + | "severeThunderstorm" + | "wreckhouseWinds" + | "lesSuetesWind" + | "avalanche" + | "flashFlood" + | "tropicalStorm" + | "inlandTropicalStorm" + | "blizzard" + | "iceStorm" + | "freezingRain" + | "debrisFlow" + | "flashFreeze" + | "dustStorm" + | "highWind" + | "winterStorm" + | "heavyFreezingSpray" + | "extremeCold" + | "windChill" + | "coldWave" + | "heavySnowAlert" + | "lakeEffectBlowingSnow" + | "snowSquall" + | "lakeEffectSnow" + | "winterWeather" + | "sleet" + | "snowfall" + | "snowAndBlowingSnow" + | "blowingSnow" + | "snowAlert" + | "arcticOutflow" + | "freezingDrizzle" + | "storm" + | "stormSurge" + | "rainfall" + | "arealFlood" + | "coastalFlood" + | "lakeshoreFlood" + | "excessiveHeat" + | "heat" + | "weather" + | "highHeatAndHumidity" + | "humidexAndHealth" + | "humidex" + | "gale" + | "freezingSpray" + | "specialMarine" + | "squall" + | "strongWind" + | "lakeWind" + | "marineWeather" + | "wind" + | "smallCraftHazardousSeas" + | "hazardousSeas" + | "smallCraft" + | "smallCraftWinds" + | "smallCraftRoughBar" + | "highWaterLevel" + | "ashfall" + | "freezingFog" + | "denseFog" + | "denseSmoke" + | "blowingDust" + | "hardFreeze" + | "freeze" + | "frost" + | "fireWeather" + | "flood" + | "ripTide" + | "highSurf" + | "smog" + | "airQuality" + | "briskWind" + | "airStagnation" + | "lowWater" + | "hydrological" + | "specialWeather"; + + /** number of seconds into the day since 00:00:00 UTC */ + export type TimeIntoDay = number; + + /** number of seconds into the day since local 00:00:00 */ + export type LocaltimeIntoDay = number; + + export type StrokeType = number + | "noEvent" + | "other" /** stroke was detected but cannot be identified */ + | "serve" + | "forehand" + | "backhand" + | "smash"; + + export type BodyLocation = number + | "leftLeg" + | "leftCalf" + | "leftShin" + | "leftHamstring" + | "leftQuad" + | "leftGlute" + | "rightLeg" + | "rightCalf" + | "rightShin" + | "rightHamstring" + | "rightQuad" + | "rightGlute" + | "torsoBack" + | "leftLowerBack" + | "leftUpperBack" + | "rightLowerBack" + | "rightUpperBack" + | "torsoFront" + | "leftAbdomen" + | "leftChest" + | "rightAbdomen" + | "rightChest" + | "leftArm" + | "leftShoulder" + | "leftBicep" + | "leftTricep" + | "leftBrachioradialis" /** Left anterior forearm */ + | "leftForearmExtensors" /** Left posterior forearm */ + | "rightArm" + | "rightShoulder" + | "rightBicep" + | "rightTricep" + | "rightBrachioradialis" /** Right anterior forearm */ + | "rightForearmExtensors" /** Right posterior forearm */ + | "neck" + | "throat" + | "waistMidBack" + | "waistFront" + | "waistLeft" + | "waistRight"; + + export type SegmentLapStatus = number + | "end" + | "fail"; + + export type SegmentLeaderboardType = number + | "overall" + | "personalBest" + | "connections" + | "group" + | "challenger" + | "kom" + | "qom" + | "pr" + | "goal" + | "carrot" + | "clubLeader" + | "rival" + | "last" + | "recentBest" + | "courseRecord"; + + export type SegmentDeleteStatus = number + | "doNotDelete" + | "deleteOne" + | "deleteAll"; + + export type SegmentSelectionType = number + | "starred" + | "suggested"; + + export type SourceType = number + | "ant" /** External device connected with ANT */ + | "antplus" /** External device connected with ANT+ */ + | "bluetooth" /** External device connected with BT */ + | "bluetoothLowEnergy" /** External device connected with BLE */ + | "wifi" /** External device connected with Wifi */ + | "local" /** Onboard device */; + + export type LocalDeviceType = number + | "gps" /** Onboard gps receiver */ + | "glonass" /** Onboard glonass receiver */ + | "gpsGlonass" /** Onboard gps glonass receiver */ + | "accelerometer" /** Onboard sensor */ + | "barometer" /** Onboard sensor */ + | "temperature" /** Onboard sensor */ + | "whr" /** Onboard wrist HR sensor */ + | "sensorHub" /** Onboard software package */; + + export type BleDeviceType = number + | "connectedGps" /** GPS that is provided over a proprietary bluetooth service */ + | "heartRate" + | "bikePower" + | "bikeSpeedCadence" + | "bikeSpeed" + | "bikeCadence" + | "footpod" + | "bikeTrainer" /** Indoor-Bike FTMS protocol */; + + export type AntChannelId = number + | "antExtendedDeviceNumberUpperNibble" + | "antTransmissionTypeLowerNibble" + | "antDeviceType" + | "antDeviceNumber"; + + export type DisplayOrientation = number + | "auto" /** automatic if the device supports it */ + | "portrait" + | "landscape" + | "portraitFlipped" /** portrait mode but rotated 180 degrees */ + | "landscapeFlipped" /** landscape mode but rotated 180 degrees */; + + export type WorkoutEquipment = number + | "none" + | "swimFins" + | "swimKickboard" + | "swimPaddles" + | "swimPullBuoy" + | "swimSnorkel"; + + export type WatchfaceMode = number + | "digital" + | "analog" + | "connectIq" + | "disabled"; + + export type DigitalWatchfaceLayout = number + | "traditional" + | "modern" + | "bold"; + + export type AnalogWatchfaceLayout = number + | "minimal" + | "traditional" + | "modern"; + + export type RiderPositionType = number + | "seated" + | "standing" + | "transitionToSeated" + | "transitionToStanding"; + + export type PowerPhaseType = number + | "powerPhaseStartAngle" + | "powerPhaseEndAngle" + | "powerPhaseArcLength" + | "powerPhaseCenter"; + + export type CameraEventType = number + | "videoStart" /** Start of video recording */ + | "videoSplit" /** Mark of video file split (end of one file, beginning of the other) */ + | "videoEnd" /** End of video recording */ + | "photoTaken" /** Still photo taken */ + | "videoSecondStreamStart" + | "videoSecondStreamSplit" + | "videoSecondStreamEnd" + | "videoSplitStart" /** Mark of video file split start */ + | "videoSecondStreamSplitStart" + | "videoPause" /** Mark when a video recording has been paused */ + | "videoSecondStreamPause" + | "videoResume" /** Mark when a video recording has been resumed */ + | "videoSecondStreamResume"; + + export type SensorType = number + | "accelerometer" + | "gyroscope" + | "compass" /** Magnetometer */ + | "barometer"; + + export type BikeLightNetworkConfigType = number + | "auto" + | "individual" + | "highVisibility" + | "trail"; + + export type CommTimeoutType = number + | "wildcardPairingTimeout" /** Timeout pairing to any device */ + | "pairingTimeout" /** Timeout pairing to previously paired device */ + | "connectionLost" /** Temporary loss of communications */ + | "connectionTimeout" /** Connection closed due to extended bad communications */; + + export type CameraOrientationType = number + | "cameraOrientation0" + | "cameraOrientation90" + | "cameraOrientation180" + | "cameraOrientation270"; + + export type AttitudeStage = number + | "failed" + | "aligning" + | "degraded" + | "valid"; + + export type AttitudeValidity = number + | "trackAngleHeadingValid" + | "pitchValid" + | "rollValid" + | "lateralBodyAccelValid" + | "normalBodyAccelValid" + | "turnRateValid" + | "hwFail" + | "magInvalid" + | "noGps" + | "gpsInvalid" + | "solutionCoasting" + | "trueTrackAngle" + | "magneticHeading"; + + export type AutoSyncFrequency = number + | "never" + | "occasionally" + | "frequent" + | "onceADay" + | "remote"; + + export type ExdLayout = number + | "fullScreen" + | "halfVertical" + | "halfHorizontal" + | "halfVerticalRightSplit" + | "halfHorizontalBottomSplit" + | "fullQuarterSplit" + | "halfVerticalLeftSplit" + | "halfHorizontalTopSplit" + | "dynamic" /** The EXD may display the configured concepts in any layout it sees fit. */; + + export type ExdDisplayType = number + | "numerical" + | "simple" + | "graph" + | "bar" + | "circleGraph" + | "virtualPartner" + | "balance" + | "stringList" + | "string" + | "simpleDynamicIcon" + | "gauge"; + + export type ExdDataUnits = number + | "noUnits" + | "laps" + | "milesPerHour" + | "kilometersPerHour" + | "feetPerHour" + | "metersPerHour" + | "degreesCelsius" + | "degreesFarenheit" + | "zone" + | "gear" + | "rpm" + | "bpm" + | "degrees" + | "millimeters" + | "meters" + | "kilometers" + | "feet" + | "yards" + | "kilofeet" + | "miles" + | "time" + | "enumTurnType" + | "percent" + | "watts" + | "wattsPerKilogram" + | "enumBatteryStatus" + | "enumBikeLightBeamAngleMode" + | "enumBikeLightBatteryStatus" + | "enumBikeLightNetworkConfigType" + | "lights" + | "seconds" + | "minutes" + | "hours" + | "calories" + | "kilojoules" + | "milliseconds" + | "secondPerMile" + | "secondPerKilometer" + | "centimeter" + | "enumCoursePoint" + | "bradians" + | "enumSport" + | "inchesHg" + | "mmHg" + | "mbars" + | "hectoPascals" + | "feetPerMin" + | "metersPerMin" + | "metersPerSec" + | "eightCardinal"; + + export type ExdQualifiers = number + | "noQualifier" + | "instantaneous" + | "average" + | "lap" + | "maximum" + | "maximumAverage" + | "maximumLap" + | "lastLap" + | "averageLap" + | "toDestination" + | "toGo" + | "toNext" + | "nextCoursePoint" + | "total" + | "threeSecondAverage" + | "tenSecondAverage" + | "thirtySecondAverage" + | "percentMaximum" + | "percentMaximumAverage" + | "lapPercentMaximum" + | "elapsed" + | "sunrise" + | "sunset" + | "comparedToVirtualPartner" + | "maximum24h" + | "minimum24h" + | "minimum" + | "first" + | "second" + | "third" + | "shifter" + | "lastSport" + | "moving" + | "stopped" + | "estimatedTotal" + | "zone9" + | "zone8" + | "zone7" + | "zone6" + | "zone5" + | "zone4" + | "zone3" + | "zone2" + | "zone1"; + + export type ExdDescriptors = number + | "bikeLightBatteryStatus" + | "beamAngleStatus" + | "bateryLevel" + | "lightNetworkMode" + | "numberLightsConnected" + | "cadence" + | "distance" + | "estimatedTimeOfArrival" + | "heading" + | "time" + | "batteryLevel" + | "trainerResistance" + | "trainerTargetPower" + | "timeSeated" + | "timeStanding" + | "elevation" + | "grade" + | "ascent" + | "descent" + | "verticalSpeed" + | "di2BatteryLevel" + | "frontGear" + | "rearGear" + | "gearRatio" + | "heartRate" + | "heartRateZone" + | "timeInHeartRateZone" + | "heartRateReserve" + | "calories" + | "gpsAccuracy" + | "gpsSignalStrength" + | "temperature" + | "timeOfDay" + | "balance" + | "pedalSmoothness" + | "power" + | "functionalThresholdPower" + | "intensityFactor" + | "work" + | "powerRatio" + | "normalizedPower" + | "trainingStressScore" + | "timeOnZone" + | "speed" + | "laps" + | "reps" + | "workoutStep" + | "courseDistance" + | "navigationDistance" + | "courseEstimatedTimeOfArrival" + | "navigationEstimatedTimeOfArrival" + | "courseTime" + | "navigationTime" + | "courseHeading" + | "navigationHeading" + | "powerZone" + | "torqueEffectiveness" + | "timerTime" + | "powerWeightRatio" + | "leftPlatformCenterOffset" + | "rightPlatformCenterOffset" + | "leftPowerPhaseStartAngle" + | "rightPowerPhaseStartAngle" + | "leftPowerPhaseFinishAngle" + | "rightPowerPhaseFinishAngle" + | "gears" /** Combined gear information */ + | "pace" + | "trainingEffect" + | "verticalOscillation" + | "verticalRatio" + | "groundContactTime" + | "leftGroundContactTimeBalance" + | "rightGroundContactTimeBalance" + | "strideLength" + | "runningCadence" + | "performanceCondition" + | "courseType" + | "timeInPowerZone" + | "navigationTurn" + | "courseLocation" + | "navigationLocation" + | "compass" + | "gearCombo" + | "muscleOxygen" + | "icon" + | "compassHeading" + | "gpsHeading" + | "gpsElevation" + | "anaerobicTrainingEffect" + | "course" + | "offCourse" + | "glideRatio" + | "verticalDistance" + | "vmg" + | "ambientPressure" + | "pressure" + | "vam"; + + export type AutoActivityDetect = number + | "none" + | "running" + | "cycling" + | "swimming" + | "walking" + | "elliptical" + | "sedentary"; + + export type SupportedExdScreenLayouts = number + | "fullScreen" + | "halfVertical" + | "halfHorizontal" + | "halfVerticalRightSplit" + | "halfHorizontalBottomSplit" + | "fullQuarterSplit" + | "halfVerticalLeftSplit" + | "halfHorizontalTopSplit"; + + export type FitBaseType = number + | "enum" + | "sint8" + | "uint8" + | "sint16" + | "uint16" + | "sint32" + | "uint32" + | "string" + | "float32" + | "float64" + | "uint8z" + | "uint16z" + | "uint32z" + | "byte" + | "sint64" + | "uint64" + | "uint64z"; + + export type TurnType = number + | "arrivingIdx" + | "arrivingLeftIdx" + | "arrivingRightIdx" + | "arrivingViaIdx" + | "arrivingViaLeftIdx" + | "arrivingViaRightIdx" + | "bearKeepLeftIdx" + | "bearKeepRightIdx" + | "continueIdx" + | "exitLeftIdx" + | "exitRightIdx" + | "ferryIdx" + | "roundabout45Idx" + | "roundabout90Idx" + | "roundabout135Idx" + | "roundabout180Idx" + | "roundabout225Idx" + | "roundabout270Idx" + | "roundabout315Idx" + | "roundabout360Idx" + | "roundaboutNeg45Idx" + | "roundaboutNeg90Idx" + | "roundaboutNeg135Idx" + | "roundaboutNeg180Idx" + | "roundaboutNeg225Idx" + | "roundaboutNeg270Idx" + | "roundaboutNeg315Idx" + | "roundaboutNeg360Idx" + | "roundaboutGenericIdx" + | "roundaboutNegGenericIdx" + | "sharpTurnLeftIdx" + | "sharpTurnRightIdx" + | "turnLeftIdx" + | "turnRightIdx" + | "uturnLeftIdx" + | "uturnRightIdx" + | "iconInvIdx" + | "iconIdxCnt"; + + export type BikeLightBeamAngleMode = number + | "manual" + | "auto"; + + export type FitBaseUnit = number + | "other" + | "kilogram" + | "pound"; + + export type SetType = number + | "rest" + | "active"; + + export type MaxMetCategory = number + | "generic" + | "cycling"; + + export type ExerciseCategory = number + | "benchPress" + | "calfRaise" + | "cardio" + | "carry" + | "chop" + | "core" + | "crunch" + | "curl" + | "deadlift" + | "flye" + | "hipRaise" + | "hipStability" + | "hipSwing" + | "hyperextension" + | "lateralRaise" + | "legCurl" + | "legRaise" + | "lunge" + | "olympicLift" + | "plank" + | "plyo" + | "pullUp" + | "pushUp" + | "row" + | "shoulderPress" + | "shoulderStability" + | "shrug" + | "sitUp" + | "squat" + | "totalBody" + | "tricepsExtension" + | "warmUp" + | "run" + | "bike" + | "cardioSensors" /** Exercises within workouts that use GPS/sensors rather than rep counting */ + | "move" + | "pose" + | "bandedExercises" + | "battleRope" + | "elliptical" + | "floorClimb" + | "indoorBike" + | "indoorRow" + | "ladder" + | "sandbag" + | "sled" + | "sledgeHammer" + | "stairStepper" + | "suspension" + | "tire" + | "runIndoor" + | "bikeOutdoor" + | "unknown"; + + export type BenchPressExerciseName = number + | "alternatingDumbbellChestPressOnSwissBall" + | "barbellBenchPress" + | "barbellBoardBenchPress" + | "barbellFloorPress" + | "closeGripBarbellBenchPress" + | "declineDumbbellBenchPress" + | "dumbbellBenchPress" + | "dumbbellFloorPress" + | "inclineBarbellBenchPress" + | "inclineDumbbellBenchPress" + | "inclineSmithMachineBenchPress" + | "isometricBarbellBenchPress" + | "kettlebellChestPress" + | "neutralGripDumbbellBenchPress" + | "neutralGripDumbbellInclineBenchPress" + | "oneArmFloorPress" + | "weightedOneArmFloorPress" + | "partialLockout" + | "reverseGripBarbellBenchPress" + | "reverseGripInclineBenchPress" + | "singleArmCableChestPress" + | "singleArmDumbbellBenchPress" + | "smithMachineBenchPress" + | "swissBallDumbbellChestPress" + | "tripleStopBarbellBenchPress" + | "wideGripBarbellBenchPress" + | "alternatingDumbbellChestPress"; + + export type CalfRaiseExerciseName = number + | "3WayCalfRaise" + | "3WayWeightedCalfRaise" + | "3WaySingleLegCalfRaise" + | "3WayWeightedSingleLegCalfRaise" + | "donkeyCalfRaise" + | "weightedDonkeyCalfRaise" + | "seatedCalfRaise" + | "weightedSeatedCalfRaise" + | "seatedDumbbellToeRaise" + | "singleLegBentKneeCalfRaise" + | "weightedSingleLegBentKneeCalfRaise" + | "singleLegDeclinePushUp" + | "singleLegDonkeyCalfRaise" + | "weightedSingleLegDonkeyCalfRaise" + | "singleLegHipRaiseWithKneeHold" + | "singleLegStandingCalfRaise" + | "singleLegStandingDumbbellCalfRaise" + | "standingBarbellCalfRaise" + | "standingCalfRaise" + | "weightedStandingCalfRaise" + | "standingDumbbellCalfRaise"; + + export type CardioExerciseName = number + | "bobAndWeaveCircle" + | "weightedBobAndWeaveCircle" + | "cardioCoreCrawl" + | "weightedCardioCoreCrawl" + | "doubleUnder" + | "weightedDoubleUnder" + | "jumpRope" + | "weightedJumpRope" + | "jumpRopeCrossover" + | "weightedJumpRopeCrossover" + | "jumpRopeJog" + | "weightedJumpRopeJog" + | "jumpingJacks" + | "weightedJumpingJacks" + | "skiMoguls" + | "weightedSkiMoguls" + | "splitJacks" + | "weightedSplitJacks" + | "squatJacks" + | "weightedSquatJacks" + | "tripleUnder" + | "weightedTripleUnder" + | "elliptical" + | "spinning" + | "polePaddleForwardWheelchair" + | "polePaddleBackwardWheelchair" + | "poleHandcycleForwardWheelchair" + | "poleHandcycleBackwardWheelchair" + | "poleRainbowWheelchair" + | "doublePunchForwardWheelchair" + | "doublePunchDownWheelchair" + | "doublePunchSidewaysWheelchair" + | "doublePunchUpWheelchair" + | "sitSkiWheelchair" + | "sittingJacksWheelchair" + | "punchForwardWheelchair" + | "punchDownWheelchair" + | "punchSidewaysWheelchair" + | "punchUpWheelchair" + | "punchBagWheelchair" + | "poleDdFfUuWheelchair" + | "butterflyArmsWheelchair" + | "punch"; + + export type CarryExerciseName = number + | "barHolds" + | "farmersWalk" + | "farmersWalkOnToes" + | "hexDumbbellHold" + | "overheadCarry" + | "dumbbellWaiterCarry" + | "farmersCarryWalkLunge" + | "farmersCarry" + | "farmersCarryOnToes"; + + export type ChopExerciseName = number + | "cablePullThrough" + | "cableRotationalLift" + | "cableWoodchop" + | "crossChopToKnee" + | "weightedCrossChopToKnee" + | "dumbbellChop" + | "halfKneelingRotation" + | "weightedHalfKneelingRotation" + | "halfKneelingRotationalChop" + | "halfKneelingRotationalReverseChop" + | "halfKneelingStabilityChop" + | "halfKneelingStabilityReverseChop" + | "kneelingRotationalChop" + | "kneelingRotationalReverseChop" + | "kneelingStabilityChop" + | "kneelingWoodchopper" + | "medicineBallWoodChops" + | "powerSquatChops" + | "weightedPowerSquatChops" + | "standingRotationalChop" + | "standingSplitRotationalChop" + | "standingSplitRotationalReverseChop" + | "standingStabilityReverseChop"; + + export type CoreExerciseName = number + | "absJabs" + | "weightedAbsJabs" + | "alternatingPlateReach" + | "barbellRollout" + | "weightedBarbellRollout" + | "bodyBarObliqueTwist" + | "cableCorePress" + | "cableSideBend" + | "sideBend" + | "weightedSideBend" + | "crescentCircle" + | "weightedCrescentCircle" + | "cyclingRussianTwist" + | "weightedCyclingRussianTwist" + | "elevatedFeetRussianTwist" + | "weightedElevatedFeetRussianTwist" + | "halfTurkishGetUp" + | "kettlebellWindmill" + | "kneelingAbWheel" + | "weightedKneelingAbWheel" + | "modifiedFrontLever" + | "openKneeTucks" + | "weightedOpenKneeTucks" + | "sideAbsLegLift" + | "weightedSideAbsLegLift" + | "swissBallJackknife" + | "weightedSwissBallJackknife" + | "swissBallPike" + | "weightedSwissBallPike" + | "swissBallRollout" + | "weightedSwissBallRollout" + | "triangleHipPress" + | "weightedTriangleHipPress" + | "trxSuspendedJackknife" + | "weightedTrxSuspendedJackknife" + | "uBoat" + | "weightedUBoat" + | "windmillSwitches" + | "weightedWindmillSwitches" + | "alternatingSlideOut" + | "weightedAlternatingSlideOut" + | "ghdBackExtensions" + | "weightedGhdBackExtensions" + | "overheadWalk" + | "inchworm" + | "weightedModifiedFrontLever" + | "russianTwist" + | "abdominalLegRotations" /** Deprecated do not use */ + | "armAndLegExtensionOnKnees" + | "bicycle" + | "bicepCurlWithLegExtension" + | "catCow" + | "corkscrew" + | "crissCross" + | "crissCrossWithBall" /** Deprecated do not use */ + | "doubleLegStretch" + | "kneeFolds" + | "lowerLift" + | "neckPull" + | "pelvicClocks" + | "rollOver" + | "rollUp" + | "rolling" + | "rowing1" + | "rowing2" + | "scissors" + | "singleLegCircles" + | "singleLegStretch" + | "snakeTwist1And2" /** Deprecated do not use */ + | "swan" + | "swimming" + | "teaser" + | "theHundred" + | "bicepCurlWithLegExtensionWithWeights" + | "hangingLSit" + | "lowerLiftWithWeights" + | "ringLSit" + | "rowing1WithWeights" + | "rowing2WithWeights" + | "scissorsWithWeights" + | "singleLegStretchWithWeights" + | "toesToElbows" + | "weightedCrissCross" + | "weightedDoubleLegStretch" + | "weightedTheHundred" + | "lSit" + | "turkishGetUp" + | "weightedRingLSit" + | "weightedHangingLSit" + | "weightedLSit" + | "sideBendLowWheelchair" + | "sideBendMidWheelchair" + | "sideBendHighWheelchair" + | "seatedSideBend"; + + export type CrunchExerciseName = number + | "bicycleCrunch" + | "cableCrunch" + | "circularArmCrunch" + | "crossedArmsCrunch" + | "weightedCrossedArmsCrunch" + | "crossLegReverseCrunch" + | "weightedCrossLegReverseCrunch" + | "crunchChop" + | "weightedCrunchChop" + | "doubleCrunch" + | "weightedDoubleCrunch" + | "elbowToKneeCrunch" + | "weightedElbowToKneeCrunch" + | "flutterKicks" + | "weightedFlutterKicks" + | "foamRollerReverseCrunchOnBench" + | "weightedFoamRollerReverseCrunchOnBench" + | "foamRollerReverseCrunchWithDumbbell" + | "foamRollerReverseCrunchWithMedicineBall" + | "frogPress" + | "hangingKneeRaiseObliqueCrunch" + | "weightedHangingKneeRaiseObliqueCrunch" + | "hipCrossover" + | "weightedHipCrossover" + | "hollowRock" + | "weightedHollowRock" + | "inclineReverseCrunch" + | "weightedInclineReverseCrunch" + | "kneelingCableCrunch" + | "kneelingCrossCrunch" + | "weightedKneelingCrossCrunch" + | "kneelingObliqueCableCrunch" + | "kneesToElbow" + | "legExtensions" + | "weightedLegExtensions" + | "legLevers" + | "mcgillCurlUp" + | "weightedMcgillCurlUp" + | "modifiedPilatesRollUpWithBall" + | "weightedModifiedPilatesRollUpWithBall" + | "pilatesCrunch" + | "weightedPilatesCrunch" + | "pilatesRollUpWithBall" + | "weightedPilatesRollUpWithBall" + | "raisedLegsCrunch" + | "weightedRaisedLegsCrunch" + | "reverseCrunch" + | "weightedReverseCrunch" + | "reverseCrunchOnABench" + | "weightedReverseCrunchOnABench" + | "reverseCurlAndLift" + | "weightedReverseCurlAndLift" + | "rotationalLift" + | "weightedRotationalLift" + | "seatedAlternatingReverseCrunch" + | "weightedSeatedAlternatingReverseCrunch" + | "seatedLegU" + | "weightedSeatedLegU" + | "sideToSideCrunchAndWeave" + | "weightedSideToSideCrunchAndWeave" + | "singleLegReverseCrunch" + | "weightedSingleLegReverseCrunch" + | "skaterCrunchCross" + | "weightedSkaterCrunchCross" + | "standingCableCrunch" + | "standingSideCrunch" + | "stepClimb" + | "weightedStepClimb" + | "swissBallCrunch" + | "swissBallReverseCrunch" + | "weightedSwissBallReverseCrunch" + | "swissBallRussianTwist" + | "weightedSwissBallRussianTwist" + | "swissBallSideCrunch" + | "weightedSwissBallSideCrunch" + | "thoracicCrunchesOnFoamRoller" + | "weightedThoracicCrunchesOnFoamRoller" + | "tricepsCrunch" + | "weightedBicycleCrunch" + | "weightedCrunch" + | "weightedSwissBallCrunch" + | "toesToBar" + | "weightedToesToBar" + | "crunch" + | "straightLegCrunchWithBall" + | "legClimbCrunch"; + + export type CurlExerciseName = number + | "alternatingDumbbellBicepsCurl" + | "alternatingDumbbellBicepsCurlOnSwissBall" + | "alternatingInclineDumbbellBicepsCurl" + | "barbellBicepsCurl" + | "barbellReverseWristCurl" + | "barbellWristCurl" + | "behindTheBackBarbellReverseWristCurl" + | "behindTheBackOneArmCableCurl" + | "cableBicepsCurl" + | "cableHammerCurl" + | "cheatingBarbellBicepsCurl" + | "closeGripEzBarBicepsCurl" + | "crossBodyDumbbellHammerCurl" + | "deadHangBicepsCurl" + | "declineHammerCurl" + | "dumbbellBicepsCurlWithStaticHold" + | "dumbbellHammerCurl" + | "dumbbellReverseWristCurl" + | "dumbbellWristCurl" + | "ezBarPreacherCurl" + | "forwardBendBicepsCurl" + | "hammerCurlToPress" + | "inclineDumbbellBicepsCurl" + | "inclineOffsetThumbDumbbellCurl" + | "kettlebellBicepsCurl" + | "lyingConcentrationCableCurl" + | "oneArmPreacherCurl" + | "platePinchCurl" + | "preacherCurlWithCable" + | "reverseEzBarCurl" + | "reverseGripWristCurl" + | "reverseGripBarbellBicepsCurl" + | "seatedAlternatingDumbbellBicepsCurl" + | "seatedDumbbellBicepsCurl" + | "seatedReverseDumbbellCurl" + | "splitStanceOffsetPinkyDumbbellCurl" + | "standingAlternatingDumbbellCurls" + | "standingDumbbellBicepsCurl" + | "standingEzBarBicepsCurl" + | "staticCurl" + | "swissBallDumbbellOverheadTricepsExtension" + | "swissBallEzBarPreacherCurl" + | "twistingStandingDumbbellBicepsCurl" + | "wideGripEzBarBicepsCurl" + | "oneArmConcentrationCurl" + | "standingZottmanBicepsCurl" + | "dumbbellBicepsCurl" + | "dragCurlWheelchair" + | "dumbbellBicepsCurlWheelchair" + | "bottleCurl" + | "seatedBottleCurl"; + + export type DeadliftExerciseName = number + | "barbellDeadlift" + | "barbellStraightLegDeadlift" + | "dumbbellDeadlift" + | "dumbbellSingleLegDeadliftToRow" + | "dumbbellStraightLegDeadlift" + | "kettlebellFloorToShelf" + | "oneArmOneLegDeadlift" + | "rackPull" + | "rotationalDumbbellStraightLegDeadlift" + | "singleArmDeadlift" + | "singleLegBarbellDeadlift" + | "singleLegBarbellStraightLegDeadlift" + | "singleLegDeadliftWithBarbell" + | "singleLegRdlCircuit" + | "singleLegRomanianDeadliftWithDumbbell" + | "sumoDeadlift" + | "sumoDeadliftHighPull" + | "trapBarDeadlift" + | "wideGripBarbellDeadlift" + | "kettlebellDeadlift" + | "kettlebellSumoDeadlift" + | "romanianDeadlift" + | "singleLegRomanianDeadliftCircuit" + | "straightLegDeadlift"; + + export type FlyeExerciseName = number + | "cableCrossover" + | "declineDumbbellFlye" + | "dumbbellFlye" + | "inclineDumbbellFlye" + | "kettlebellFlye" + | "kneelingRearFlye" + | "singleArmStandingCableReverseFlye" + | "swissBallDumbbellFlye" + | "armRotations" + | "hugATree" + | "faceDownInclineReverseFlye" + | "inclineReverseFlye" + | "rearDeltFlyWheelchair"; + + export type HipRaiseExerciseName = number + | "barbellHipThrustOnFloor" + | "barbellHipThrustWithBench" + | "bentKneeSwissBallReverseHipRaise" + | "weightedBentKneeSwissBallReverseHipRaise" + | "bridgeWithLegExtension" + | "weightedBridgeWithLegExtension" + | "clamBridge" + | "frontKickTabletop" + | "weightedFrontKickTabletop" + | "hipExtensionAndCross" + | "weightedHipExtensionAndCross" + | "hipRaise" + | "weightedHipRaise" + | "hipRaiseWithFeetOnSwissBall" + | "weightedHipRaiseWithFeetOnSwissBall" + | "hipRaiseWithHeadOnBosuBall" + | "weightedHipRaiseWithHeadOnBosuBall" + | "hipRaiseWithHeadOnSwissBall" + | "weightedHipRaiseWithHeadOnSwissBall" + | "hipRaiseWithKneeSqueeze" + | "weightedHipRaiseWithKneeSqueeze" + | "inclineRearLegExtension" + | "weightedInclineRearLegExtension" + | "kettlebellSwing" + | "marchingHipRaise" + | "weightedMarchingHipRaise" + | "marchingHipRaiseWithFeetOnASwissBall" + | "weightedMarchingHipRaiseWithFeetOnASwissBall" + | "reverseHipRaise" + | "weightedReverseHipRaise" + | "singleLegHipRaise" + | "weightedSingleLegHipRaise" + | "singleLegHipRaiseWithFootOnBench" + | "weightedSingleLegHipRaiseWithFootOnBench" + | "singleLegHipRaiseWithFootOnBosuBall" + | "weightedSingleLegHipRaiseWithFootOnBosuBall" + | "singleLegHipRaiseWithFootOnFoamRoller" + | "weightedSingleLegHipRaiseWithFootOnFoamRoller" + | "singleLegHipRaiseWithFootOnMedicineBall" + | "weightedSingleLegHipRaiseWithFootOnMedicineBall" + | "singleLegHipRaiseWithHeadOnBosuBall" + | "weightedSingleLegHipRaiseWithHeadOnBosuBall" + | "weightedClamBridge" + | "singleLegSwissBallHipRaiseAndLegCurl" + | "clams" + | "innerThighCircles" /** Deprecated do not use */ + | "innerThighSideLift" /** Deprecated do not use */ + | "legCircles" + | "legLift" + | "legLiftInExternalRotation"; + + export type HipStabilityExerciseName = number + | "bandSideLyingLegRaise" + | "deadBug" + | "weightedDeadBug" + | "externalHipRaise" + | "weightedExternalHipRaise" + | "fireHydrantKicks" + | "weightedFireHydrantKicks" + | "hipCircles" + | "weightedHipCircles" + | "innerThighLift" + | "weightedInnerThighLift" + | "lateralWalksWithBandAtAnkles" + | "pretzelSideKick" + | "weightedPretzelSideKick" + | "proneHipInternalRotation" + | "weightedProneHipInternalRotation" + | "quadruped" + | "quadrupedHipExtension" + | "weightedQuadrupedHipExtension" + | "quadrupedWithLegLift" + | "weightedQuadrupedWithLegLift" + | "sideLyingLegRaise" + | "weightedSideLyingLegRaise" + | "slidingHipAdduction" + | "weightedSlidingHipAdduction" + | "standingAdduction" + | "weightedStandingAdduction" + | "standingCableHipAbduction" + | "standingHipAbduction" + | "weightedStandingHipAbduction" + | "standingRearLegRaise" + | "weightedStandingRearLegRaise" + | "supineHipInternalRotation" + | "weightedSupineHipInternalRotation" + | "lyingAbductionStretch"; + + export type HipSwingExerciseName = number + | "singleArmKettlebellSwing" + | "singleArmDumbbellSwing" + | "stepOutSwing" + | "oneArmSwing"; + + export type HyperextensionExerciseName = number + | "backExtensionWithOppositeArmAndLegReach" + | "weightedBackExtensionWithOppositeArmAndLegReach" + | "baseRotations" + | "weightedBaseRotations" + | "bentKneeReverseHyperextension" + | "weightedBentKneeReverseHyperextension" + | "hollowHoldAndRoll" + | "weightedHollowHoldAndRoll" + | "kicks" + | "weightedKicks" + | "kneeRaises" + | "weightedKneeRaises" + | "kneelingSuperman" + | "weightedKneelingSuperman" + | "latPullDownWithRow" + | "medicineBallDeadliftToReach" + | "oneArmOneLegRow" + | "oneArmRowWithBand" + | "overheadLungeWithMedicineBall" + | "plankKneeTucks" + | "weightedPlankKneeTucks" + | "sideStep" + | "weightedSideStep" + | "singleLegBackExtension" + | "weightedSingleLegBackExtension" + | "spineExtension" + | "weightedSpineExtension" + | "staticBackExtension" + | "weightedStaticBackExtension" + | "supermanFromFloor" + | "weightedSupermanFromFloor" + | "swissBallBackExtension" + | "weightedSwissBallBackExtension" + | "swissBallHyperextension" + | "weightedSwissBallHyperextension" + | "swissBallOppositeArmAndLegLift" + | "weightedSwissBallOppositeArmAndLegLift" + | "supermanOnSwissBall" + | "cobra" + | "supineFloorBarre" /** Deprecated do not use */; + + export type LateralRaiseExerciseName = number + | "45DegreeCableExternalRotation" + | "alternatingLateralRaiseWithStaticHold" + | "barMuscleUp" + | "bentOverLateralRaise" + | "cableDiagonalRaise" + | "cableFrontRaise" + | "calorieRow" + | "comboShoulderRaise" + | "dumbbellDiagonalRaise" + | "dumbbellVRaise" + | "frontRaise" + | "leaningDumbbellLateralRaise" + | "lyingDumbbellRaise" + | "muscleUp" + | "oneArmCableLateralRaise" + | "overhandGripRearLateralRaise" + | "plateRaises" + | "ringDip" + | "weightedRingDip" + | "ringMuscleUp" + | "weightedRingMuscleUp" + | "ropeClimb" + | "weightedRopeClimb" + | "scaption" + | "seatedLateralRaise" + | "seatedRearLateralRaise" + | "sideLyingLateralRaise" + | "standingLift" + | "suspendedRow" + | "underhandGripRearLateralRaise" + | "wallSlide" + | "weightedWallSlide" + | "armCircles" + | "shavingTheHead" + | "dumbbellLateralRaise" + | "ringDipKipping" + | "wallWalk" + | "dumbbellFrontRaiseWheelchair" + | "dumbbellLateralRaiseWheelchair" + | "poleDoubleArmOverheadAndForwardWheelchair" + | "poleStraightArmOverheadWheelchair"; + + export type LegCurlExerciseName = number + | "legCurl" + | "weightedLegCurl" + | "goodMorning" + | "seatedBarbellGoodMorning" + | "singleLegBarbellGoodMorning" + | "singleLegSlidingLegCurl" + | "slidingLegCurl" + | "splitBarbellGoodMorning" + | "splitStanceExtension" + | "staggeredStanceGoodMorning" + | "swissBallHipRaiseAndLegCurl" + | "zercherGoodMorning" + | "bandGoodMorning" + | "barGoodMorning"; + + export type LegRaiseExerciseName = number + | "hangingKneeRaise" + | "hangingLegRaise" + | "weightedHangingLegRaise" + | "hangingSingleLegRaise" + | "weightedHangingSingleLegRaise" + | "kettlebellLegRaises" + | "legLoweringDrill" + | "weightedLegLoweringDrill" + | "lyingStraightLegRaise" + | "weightedLyingStraightLegRaise" + | "medicineBallLegDrops" + | "quadrupedLegRaise" + | "weightedQuadrupedLegRaise" + | "reverseLegRaise" + | "weightedReverseLegRaise" + | "reverseLegRaiseOnSwissBall" + | "weightedReverseLegRaiseOnSwissBall" + | "singleLegLoweringDrill" + | "weightedSingleLegLoweringDrill" + | "weightedHangingKneeRaise" + | "lateralStepover" + | "weightedLateralStepover"; + + export type LungeExerciseName = number + | "overheadLunge" + | "lungeMatrix" + | "weightedLungeMatrix" + | "alternatingBarbellForwardLunge" + | "alternatingDumbbellLungeWithReach" + | "backFootElevatedDumbbellSplitSquat" + | "barbellBoxLunge" + | "barbellBulgarianSplitSquat" + | "barbellCrossoverLunge" + | "barbellFrontSplitSquat" + | "barbellLunge" + | "barbellReverseLunge" + | "barbellSideLunge" + | "barbellSplitSquat" + | "coreControlRearLunge" + | "diagonalLunge" + | "dropLunge" + | "dumbbellBoxLunge" + | "dumbbellBulgarianSplitSquat" + | "dumbbellCrossoverLunge" + | "dumbbellDiagonalLunge" + | "dumbbellLunge" + | "dumbbellLungeAndRotation" + | "dumbbellOverheadBulgarianSplitSquat" + | "dumbbellReverseLungeToHighKneeAndPress" + | "dumbbellSideLunge" + | "elevatedFrontFootBarbellSplitSquat" + | "frontFootElevatedDumbbellSplitSquat" + | "gunslingerLunge" + | "lawnmowerLunge" + | "lowLungeWithIsometricAdduction" + | "lowSideToSideLunge" + | "lunge" + | "weightedLunge" + | "lungeWithArmReach" + | "lungeWithDiagonalReach" + | "lungeWithSideBend" + | "offsetDumbbellLunge" + | "offsetDumbbellReverseLunge" + | "overheadBulgarianSplitSquat" + | "overheadDumbbellReverseLunge" + | "overheadDumbbellSplitSquat" + | "overheadLungeWithRotation" + | "reverseBarbellBoxLunge" + | "reverseBoxLunge" + | "reverseDumbbellBoxLunge" + | "reverseDumbbellCrossoverLunge" + | "reverseDumbbellDiagonalLunge" + | "reverseLungeWithReachBack" + | "weightedReverseLungeWithReachBack" + | "reverseLungeWithTwistAndOverheadReach" + | "weightedReverseLungeWithTwistAndOverheadReach" + | "reverseSlidingBoxLunge" + | "weightedReverseSlidingBoxLunge" + | "reverseSlidingLunge" + | "weightedReverseSlidingLunge" + | "runnersLungeToBalance" + | "weightedRunnersLungeToBalance" + | "shiftingSideLunge" + | "sideAndCrossoverLunge" + | "weightedSideAndCrossoverLunge" + | "sideLunge" + | "weightedSideLunge" + | "sideLungeAndPress" + | "sideLungeJumpOff" + | "sideLungeSweep" + | "weightedSideLungeSweep" + | "sideLungeToCrossoverTap" + | "weightedSideLungeToCrossoverTap" + | "sideToSideLungeChops" + | "weightedSideToSideLungeChops" + | "siffJumpLunge" + | "weightedSiffJumpLunge" + | "singleArmReverseLungeAndPress" + | "slidingLateralLunge" + | "weightedSlidingLateralLunge" + | "walkingBarbellLunge" + | "walkingDumbbellLunge" + | "walkingLunge" + | "weightedWalkingLunge" + | "wideGripOverheadBarbellSplitSquat" + | "alternatingDumbbellLunge" + | "dumbbellReverseLunge" + | "overheadDumbbellLunge" + | "scissorPowerSwitch" + | "dumbbellOverheadWalkingLunge" + | "curtsyLunge" + | "weightedCurtsyLunge" + | "weightedShiftingSideLunge" + | "weightedSideLungeAndPress" + | "weightedSideLungeJumpOff"; + + export type OlympicLiftExerciseName = number + | "barbellHangPowerClean" + | "barbellHangSquatClean" + | "barbellPowerClean" + | "barbellPowerSnatch" + | "barbellSquatClean" + | "cleanAndJerk" + | "barbellHangPowerSnatch" + | "barbellHangPull" + | "barbellHighPull" + | "barbellSnatch" + | "barbellSplitJerk" + | "clean" + | "dumbbellClean" + | "dumbbellHangPull" + | "oneHandDumbbellSplitSnatch" + | "pushJerk" + | "singleArmDumbbellSnatch" + | "singleArmHangSnatch" + | "singleArmKettlebellSnatch" + | "splitJerk" + | "squatCleanAndJerk" + | "dumbbellHangSnatch" + | "dumbbellPowerCleanAndJerk" + | "dumbbellPowerCleanAndPushPress" + | "dumbbellPowerCleanAndStrictPress" + | "dumbbellSnatch" + | "medicineBallClean" + | "cleanAndPress" + | "snatch"; + + export type PlankExerciseName = number + | "45DegreePlank" + | "weighted45DegreePlank" + | "90DegreeStaticHold" + | "weighted90DegreeStaticHold" + | "bearCrawl" + | "weightedBearCrawl" + | "crossBodyMountainClimber" + | "weightedCrossBodyMountainClimber" + | "elbowPlankPikeJacks" + | "weightedElbowPlankPikeJacks" + | "elevatedFeetPlank" + | "weightedElevatedFeetPlank" + | "elevatorAbs" + | "weightedElevatorAbs" + | "extendedPlank" + | "weightedExtendedPlank" + | "fullPlankPasseTwist" + | "weightedFullPlankPasseTwist" + | "inchingElbowPlank" + | "weightedInchingElbowPlank" + | "inchwormToSidePlank" + | "weightedInchwormToSidePlank" + | "kneelingPlank" + | "weightedKneelingPlank" + | "kneelingSidePlankWithLegLift" + | "weightedKneelingSidePlankWithLegLift" + | "lateralRoll" + | "weightedLateralRoll" + | "lyingReversePlank" + | "weightedLyingReversePlank" + | "medicineBallMountainClimber" + | "weightedMedicineBallMountainClimber" + | "modifiedMountainClimberAndExtension" + | "weightedModifiedMountainClimberAndExtension" + | "mountainClimber" + | "weightedMountainClimber" + | "mountainClimberOnSlidingDiscs" + | "weightedMountainClimberOnSlidingDiscs" + | "mountainClimberWithFeetOnBosuBall" + | "weightedMountainClimberWithFeetOnBosuBall" + | "mountainClimberWithHandsOnBench" + | "mountainClimberWithHandsOnSwissBall" + | "weightedMountainClimberWithHandsOnSwissBall" + | "plank" + | "plankJacksWithFeetOnSlidingDiscs" + | "weightedPlankJacksWithFeetOnSlidingDiscs" + | "plankKneeTwist" + | "weightedPlankKneeTwist" + | "plankPikeJumps" + | "weightedPlankPikeJumps" + | "plankPikes" + | "weightedPlankPikes" + | "plankToStandUp" + | "weightedPlankToStandUp" + | "plankWithArmRaise" + | "weightedPlankWithArmRaise" + | "plankWithKneeToElbow" + | "weightedPlankWithKneeToElbow" + | "plankWithObliqueCrunch" + | "weightedPlankWithObliqueCrunch" + | "plyometricSidePlank" + | "weightedPlyometricSidePlank" + | "rollingSidePlank" + | "weightedRollingSidePlank" + | "sideKickPlank" + | "weightedSideKickPlank" + | "sidePlank" + | "weightedSidePlank" + | "sidePlankAndRow" + | "weightedSidePlankAndRow" + | "sidePlankLift" + | "weightedSidePlankLift" + | "sidePlankWithElbowOnBosuBall" + | "weightedSidePlankWithElbowOnBosuBall" + | "sidePlankWithFeetOnBench" + | "weightedSidePlankWithFeetOnBench" + | "sidePlankWithKneeCircle" + | "weightedSidePlankWithKneeCircle" + | "sidePlankWithKneeTuck" + | "weightedSidePlankWithKneeTuck" + | "sidePlankWithLegLift" + | "weightedSidePlankWithLegLift" + | "sidePlankWithReachUnder" + | "weightedSidePlankWithReachUnder" + | "singleLegElevatedFeetPlank" + | "weightedSingleLegElevatedFeetPlank" + | "singleLegFlexAndExtend" + | "weightedSingleLegFlexAndExtend" + | "singleLegSidePlank" + | "weightedSingleLegSidePlank" + | "spidermanPlank" + | "weightedSpidermanPlank" + | "straightArmPlank" + | "weightedStraightArmPlank" + | "straightArmPlankWithShoulderTouch" + | "weightedStraightArmPlankWithShoulderTouch" + | "swissBallPlank" + | "weightedSwissBallPlank" + | "swissBallPlankLegLift" + | "weightedSwissBallPlankLegLift" + | "swissBallPlankLegLiftAndHold" + | "swissBallPlankWithFeetOnBench" + | "weightedSwissBallPlankWithFeetOnBench" + | "swissBallProneJackknife" + | "weightedSwissBallProneJackknife" + | "swissBallSidePlank" + | "weightedSwissBallSidePlank" + | "threeWayPlank" + | "weightedThreeWayPlank" + | "towelPlankAndKneeIn" + | "weightedTowelPlankAndKneeIn" + | "tStabilization" + | "weightedTStabilization" + | "turkishGetUpToSidePlank" + | "weightedTurkishGetUpToSidePlank" + | "twoPointPlank" + | "weightedTwoPointPlank" + | "weightedPlank" + | "wideStancePlankWithDiagonalArmLift" + | "weightedWideStancePlankWithDiagonalArmLift" + | "wideStancePlankWithDiagonalLegLift" + | "weightedWideStancePlankWithDiagonalLegLift" + | "wideStancePlankWithLegLift" + | "weightedWideStancePlankWithLegLift" + | "wideStancePlankWithOppositeArmAndLegLift" + | "weightedMountainClimberWithHandsOnBench" + | "weightedSwissBallPlankLegLiftAndHold" + | "weightedWideStancePlankWithOppositeArmAndLegLift" + | "plankWithFeetOnSwissBall" + | "sidePlankToPlankWithReachUnder" + | "bridgeWithGluteLowerLift" + | "bridgeOneLegBridge" + | "plankWithArmVariations" + | "plankWithLegLift" + | "reversePlankWithLegPull" + | "ringPlankSprawls"; + + export type PlyoExerciseName = number + | "alternatingJumpLunge" + | "weightedAlternatingJumpLunge" + | "barbellJumpSquat" + | "bodyWeightJumpSquat" + | "weightedJumpSquat" + | "crossKneeStrike" + | "weightedCrossKneeStrike" + | "depthJump" + | "weightedDepthJump" + | "dumbbellJumpSquat" + | "dumbbellSplitJump" + | "frontKneeStrike" + | "weightedFrontKneeStrike" + | "highBoxJump" + | "weightedHighBoxJump" + | "isometricExplosiveBodyWeightJumpSquat" + | "weightedIsometricExplosiveJumpSquat" + | "lateralLeapAndHop" + | "weightedLateralLeapAndHop" + | "lateralPlyoSquats" + | "weightedLateralPlyoSquats" + | "lateralSlide" + | "weightedLateralSlide" + | "medicineBallOverheadThrows" + | "medicineBallSideThrow" + | "medicineBallSlam" + | "sideToSideMedicineBallThrows" + | "sideToSideShuffleJump" + | "weightedSideToSideShuffleJump" + | "squatJumpOntoBox" + | "weightedSquatJumpOntoBox" + | "squatJumpsInAndOut" + | "weightedSquatJumpsInAndOut" + | "boxJump" + | "boxJumpOvers" + | "boxJumpOversOverTheBox" + | "starJumpSquats" + | "jumpSquat"; + + export type PullUpExerciseName = number + | "bandedPullUps" + | "30DegreeLatPulldown" + | "bandAssistedChinUp" + | "closeGripChinUp" + | "weightedCloseGripChinUp" + | "closeGripLatPulldown" + | "crossoverChinUp" + | "weightedCrossoverChinUp" + | "ezBarPullover" + | "hangingHurdle" + | "weightedHangingHurdle" + | "kneelingLatPulldown" + | "kneelingUnderhandGripLatPulldown" + | "latPulldown" + | "mixedGripChinUp" + | "weightedMixedGripChinUp" + | "mixedGripPullUp" + | "weightedMixedGripPullUp" + | "reverseGripPulldown" + | "standingCablePullover" + | "straightArmPulldown" + | "swissBallEzBarPullover" + | "towelPullUp" + | "weightedTowelPullUp" + | "weightedPullUp" + | "wideGripLatPulldown" + | "wideGripPullUp" + | "weightedWideGripPullUp" + | "burpeePullUp" + | "weightedBurpeePullUp" + | "jumpingPullUps" + | "weightedJumpingPullUps" + | "kippingPullUp" + | "weightedKippingPullUp" + | "lPullUp" + | "weightedLPullUp" + | "suspendedChinUp" + | "weightedSuspendedChinUp" + | "pullUp" + | "chinUp" + | "neutralGripChinUp" + | "weightedChinUp" + | "bandAssistedPullUp" + | "neutralGripPullUp" + | "weightedNeutralGripChinUp" + | "weightedNeutralGripPullUp"; + + export type PushUpExerciseName = number + | "chestPressWithBand" + | "alternatingStaggeredPushUp" + | "weightedAlternatingStaggeredPushUp" + | "alternatingHandsMedicineBallPushUp" + | "weightedAlternatingHandsMedicineBallPushUp" + | "bosuBallPushUp" + | "weightedBosuBallPushUp" + | "clappingPushUp" + | "weightedClappingPushUp" + | "closeGripMedicineBallPushUp" + | "weightedCloseGripMedicineBallPushUp" + | "closeHandsPushUp" + | "weightedCloseHandsPushUp" + | "declinePushUp" + | "weightedDeclinePushUp" + | "diamondPushUp" + | "weightedDiamondPushUp" + | "explosiveCrossoverPushUp" + | "weightedExplosiveCrossoverPushUp" + | "explosivePushUp" + | "weightedExplosivePushUp" + | "feetElevatedSideToSidePushUp" + | "weightedFeetElevatedSideToSidePushUp" + | "handReleasePushUp" + | "weightedHandReleasePushUp" + | "handstandPushUp" + | "weightedHandstandPushUp" + | "inclinePushUp" + | "weightedInclinePushUp" + | "isometricExplosivePushUp" + | "weightedIsometricExplosivePushUp" + | "judoPushUp" + | "weightedJudoPushUp" + | "kneelingPushUp" + | "weightedKneelingPushUp" + | "medicineBallChestPass" + | "medicineBallPushUp" + | "weightedMedicineBallPushUp" + | "oneArmPushUp" + | "weightedOneArmPushUp" + | "weightedPushUp" + | "pushUpAndRow" + | "weightedPushUpAndRow" + | "pushUpPlus" + | "weightedPushUpPlus" + | "pushUpWithFeetOnSwissBall" + | "weightedPushUpWithFeetOnSwissBall" + | "pushUpWithOneHandOnMedicineBall" + | "weightedPushUpWithOneHandOnMedicineBall" + | "shoulderPushUp" + | "weightedShoulderPushUp" + | "singleArmMedicineBallPushUp" + | "weightedSingleArmMedicineBallPushUp" + | "spidermanPushUp" + | "weightedSpidermanPushUp" + | "stackedFeetPushUp" + | "weightedStackedFeetPushUp" + | "staggeredHandsPushUp" + | "weightedStaggeredHandsPushUp" + | "suspendedPushUp" + | "weightedSuspendedPushUp" + | "swissBallPushUp" + | "weightedSwissBallPushUp" + | "swissBallPushUpPlus" + | "weightedSwissBallPushUpPlus" + | "tPushUp" + | "weightedTPushUp" + | "tripleStopPushUp" + | "weightedTripleStopPushUp" + | "wideHandsPushUp" + | "weightedWideHandsPushUp" + | "paralletteHandstandPushUp" + | "weightedParalletteHandstandPushUp" + | "ringHandstandPushUp" + | "weightedRingHandstandPushUp" + | "ringPushUp" + | "weightedRingPushUp" + | "pushUp" + | "pilatesPushup" + | "dynamicPushUp" + | "kippingHandstandPushUp" + | "shoulderTappingPushUp" + | "bicepsPushUp" + | "hinduPushUp" + | "pikePushUp" + | "wideGripPushUp" + | "weightedBicepsPushUp" + | "weightedHinduPushUp" + | "weightedPikePushUp" + | "kippingParalletteHandstandPushUp" + | "wallPushUp"; + + export type RowExerciseName = number + | "barbellStraightLegDeadliftToRow" + | "cableRowStanding" + | "dumbbellRow" + | "elevatedFeetInvertedRow" + | "weightedElevatedFeetInvertedRow" + | "facePull" + | "facePullWithExternalRotation" + | "invertedRowWithFeetOnSwissBall" + | "weightedInvertedRowWithFeetOnSwissBall" + | "kettlebellRow" + | "modifiedInvertedRow" + | "weightedModifiedInvertedRow" + | "neutralGripAlternatingDumbbellRow" + | "oneArmBentOverRow" + | "oneLeggedDumbbellRow" + | "renegadeRow" + | "reverseGripBarbellRow" + | "ropeHandleCableRow" + | "seatedCableRow" + | "seatedDumbbellRow" + | "singleArmCableRow" + | "singleArmCableRowAndRotation" + | "singleArmInvertedRow" + | "weightedSingleArmInvertedRow" + | "singleArmNeutralGripDumbbellRow" + | "singleArmNeutralGripDumbbellRowAndRotation" + | "suspendedInvertedRow" + | "weightedSuspendedInvertedRow" + | "tBarRow" + | "towelGripInvertedRow" + | "weightedTowelGripInvertedRow" + | "underhandGripCableRow" + | "vGripCableRow" + | "wideGripSeatedCableRow" + | "alternatingDumbbellRow" + | "invertedRow" + | "row" + | "weightedRow" + | "indoorRow" + | "bandedFacePulls" + | "chestSupportedDumbbellRow" + | "declineRingRow" + | "elevatedRingRow" + | "rdlBentOverRowWithBarbellDumbbell" + | "ringRow" + | "barbellRow" + | "bentOverRowWithBarbell" + | "bentOverRowWithDumbell" + | "seatedUnderhandGripCableRow" + | "trxInvertedRow" + | "weightedInvertedRow" + | "weightedTrxInvertedRow" + | "dumbbellRowWheelchair"; + + export type ShoulderPressExerciseName = number + | "alternatingDumbbellShoulderPress" + | "arnoldPress" + | "barbellFrontSquatToPushPress" + | "barbellPushPress" + | "barbellShoulderPress" + | "deadCurlPress" + | "dumbbellAlternatingShoulderPressAndTwist" + | "dumbbellHammerCurlToLungeToPress" + | "dumbbellPushPress" + | "floorInvertedShoulderPress" + | "weightedFloorInvertedShoulderPress" + | "invertedShoulderPress" + | "weightedInvertedShoulderPress" + | "oneArmPushPress" + | "overheadBarbellPress" + | "overheadDumbbellPress" + | "seatedBarbellShoulderPress" + | "seatedDumbbellShoulderPress" + | "singleArmDumbbellShoulderPress" + | "singleArmStepUpAndPress" + | "smithMachineOverheadPress" + | "splitStanceHammerCurlToPress" + | "swissBallDumbbellShoulderPress" + | "weightPlateFrontRaise" + | "dumbbellShoulderPress" + | "militaryPress" + | "strictPress" + | "dumbbellFrontRaise" + | "dumbbellCurlToOverheadPressWheelchair" + | "arnoldPressWheelchair" + | "overheadDumbbellPressWheelchair"; + + export type ShoulderStabilityExerciseName = number + | "90DegreeCableExternalRotation" + | "bandExternalRotation" + | "bandInternalRotation" + | "bentArmLateralRaiseAndExternalRotation" + | "cableExternalRotation" + | "dumbbellFacePullWithExternalRotation" + | "floorIRaise" + | "weightedFloorIRaise" + | "floorTRaise" + | "weightedFloorTRaise" + | "floorYRaise" + | "weightedFloorYRaise" + | "inclineIRaise" + | "weightedInclineIRaise" + | "inclineLRaise" + | "weightedInclineLRaise" + | "inclineTRaise" + | "weightedInclineTRaise" + | "inclineWRaise" + | "weightedInclineWRaise" + | "inclineYRaise" + | "weightedInclineYRaise" + | "lyingExternalRotation" + | "seatedDumbbellExternalRotation" + | "standingLRaise" + | "swissBallIRaise" + | "weightedSwissBallIRaise" + | "swissBallTRaise" + | "weightedSwissBallTRaise" + | "swissBallWRaise" + | "weightedSwissBallWRaise" + | "swissBallYRaise" + | "weightedSwissBallYRaise" + | "cableInternalRotation" + | "lyingInternalRotation" + | "seatedDumbbellInternalRotation"; + + export type ShrugExerciseName = number + | "barbellJumpShrug" + | "barbellShrug" + | "barbellUprightRow" + | "behindTheBackSmithMachineShrug" + | "dumbbellJumpShrug" + | "dumbbellShrug" + | "dumbbellUprightRow" + | "inclineDumbbellShrug" + | "overheadBarbellShrug" + | "overheadDumbbellShrug" + | "scaptionAndShrug" + | "scapularRetraction" + | "serratusChairShrug" + | "weightedSerratusChairShrug" + | "serratusShrug" + | "weightedSerratusShrug" + | "wideGripJumpShrug" + | "wideGripBarbellShrug" + | "behindTheBackShrug" + | "dumbbellShrugWheelchair" + | "shrugWheelchair" + | "shrugArmDownWheelchair" + | "shrugArmMidWheelchair" + | "shrugArmUpWheelchair" + | "uprightRow"; + + export type SitUpExerciseName = number + | "alternatingSitUp" + | "weightedAlternatingSitUp" + | "bentKneeVUp" + | "weightedBentKneeVUp" + | "butterflySitUp" + | "weightedButterflySitup" + | "crossPunchRollUp" + | "weightedCrossPunchRollUp" + | "crossedArmsSitUp" + | "weightedCrossedArmsSitUp" + | "getUpSitUp" + | "weightedGetUpSitUp" + | "hoveringSitUp" + | "weightedHoveringSitUp" + | "kettlebellSitUp" + | "medicineBallAlternatingVUp" + | "medicineBallSitUp" + | "medicineBallVUp" + | "modifiedSitUp" + | "negativeSitUp" + | "oneArmFullSitUp" + | "recliningCircle" + | "weightedRecliningCircle" + | "reverseCurlUp" + | "weightedReverseCurlUp" + | "singleLegSwissBallJackknife" + | "weightedSingleLegSwissBallJackknife" + | "theTeaser" + | "theTeaserWeighted" + | "threePartRollDown" + | "weightedThreePartRollDown" + | "vUp" + | "weightedVUp" + | "weightedRussianTwistOnSwissBall" + | "weightedSitUp" + | "xAbs" + | "weightedXAbs" + | "sitUp" + | "ghdSitUps" + | "sitUpTurkishGetUp" + | "russianTwistOnSwissBall"; + + export type SquatExerciseName = number + | "legPress" + | "backSquatWithBodyBar" + | "backSquats" + | "weightedBackSquats" + | "balancingSquat" + | "weightedBalancingSquat" + | "barbellBackSquat" + | "barbellBoxSquat" + | "barbellFrontSquat" + | "barbellHackSquat" + | "barbellHangSquatSnatch" + | "barbellLateralStepUp" + | "barbellQuarterSquat" + | "barbellSiffSquat" + | "barbellSquatSnatch" + | "barbellSquatWithHeelsRaised" + | "barbellStepover" + | "barbellStepUp" + | "benchSquatWithRotationalChop" + | "weightedBenchSquatWithRotationalChop" + | "bodyWeightWallSquat" + | "weightedWallSquat" + | "boxStepSquat" + | "weightedBoxStepSquat" + | "bracedSquat" + | "crossedArmBarbellFrontSquat" + | "crossoverDumbbellStepUp" + | "dumbbellFrontSquat" + | "dumbbellSplitSquat" + | "dumbbellSquat" + | "dumbbellSquatClean" + | "dumbbellStepover" + | "dumbbellStepUp" + | "elevatedSingleLegSquat" + | "weightedElevatedSingleLegSquat" + | "figureFourSquats" + | "weightedFigureFourSquats" + | "gobletSquat" + | "kettlebellSquat" + | "kettlebellSwingOverhead" + | "kettlebellSwingWithFlipToSquat" + | "lateralDumbbellStepUp" + | "oneLeggedSquat" + | "overheadDumbbellSquat" + | "overheadSquat" + | "partialSingleLegSquat" + | "weightedPartialSingleLegSquat" + | "pistolSquat" + | "weightedPistolSquat" + | "plieSlides" + | "weightedPlieSlides" + | "plieSquat" + | "weightedPlieSquat" + | "prisonerSquat" + | "weightedPrisonerSquat" + | "singleLegBenchGetUp" + | "weightedSingleLegBenchGetUp" + | "singleLegBenchSquat" + | "weightedSingleLegBenchSquat" + | "singleLegSquatOnSwissBall" + | "weightedSingleLegSquatOnSwissBall" + | "squat" + | "weightedSquat" + | "squatsWithBand" + | "staggeredSquat" + | "weightedStaggeredSquat" + | "stepUp" + | "weightedStepUp" + | "suitcaseSquats" + | "sumoSquat" + | "sumoSquatSlideIn" + | "weightedSumoSquatSlideIn" + | "sumoSquatToHighPull" + | "sumoSquatToStand" + | "weightedSumoSquatToStand" + | "sumoSquatWithRotation" + | "weightedSumoSquatWithRotation" + | "swissBallBodyWeightWallSquat" + | "weightedSwissBallWallSquat" + | "thrusters" + | "unevenSquat" + | "weightedUnevenSquat" + | "waistSlimmingSquat" + | "wallBall" + | "wideStanceBarbellSquat" + | "wideStanceGobletSquat" + | "zercherSquat" + | "kbsOverhead" /** Deprecated do not use */ + | "squatAndSideKick" + | "squatJumpsInNOut" + | "pilatesPlieSquatsParallelTurnedOutFlatAndHeels" + | "releveStraightLegAndKneeBentWithOneLegVariation" + | "alternatingBoxDumbbellStepUps" + | "dumbbellOverheadSquatSingleArm" + | "dumbbellSquatSnatch" + | "medicineBallSquat" + | "wallBallSquatAndPress" + | "squatAmericanSwing" + | "airSquat" + | "dumbbellThrusters" + | "overheadBarbellSquat"; + + export type TotalBodyExerciseName = number + | "burpee" + | "weightedBurpee" + | "burpeeBoxJump" + | "weightedBurpeeBoxJump" + | "highPullBurpee" + | "manMakers" + | "oneArmBurpee" + | "squatThrusts" + | "weightedSquatThrusts" + | "squatPlankPushUp" + | "weightedSquatPlankPushUp" + | "standingTRotationBalance" + | "weightedStandingTRotationBalance" + | "barbellBurpee" + | "burpeeBoxJumpOverYesLiterallyJumpingOverTheBox" + | "burpeeBoxJumpStepUpOver" + | "lateralBarbellBurpee" + | "totalBodyBurpeeOverBar" + | "burpeeBoxJumpOver" + | "burpeeWheelchair"; + + export type MoveExerciseName = number + | "archAndCurl" + | "armCirclesWithBallBandAndWeight" + | "armStretch" + | "backMassage" + | "bellyBreathing" + | "bridgeWithBall" + | "diamondLegCrunch" + | "diamondLegLift" + | "eightPointShoulderOpener" + | "footRolling" + | "footwork" + | "footworkOnDisc" + | "forwardFold" + | "frogWithBand" + | "halfRollUp" + | "hamstringCurl" + | "hamstringStretch" + | "hipStretch" + | "hugATreeWithBallBandAndWeight" + | "kneeCircles" + | "kneeFoldsOnDisc" + | "lateralFlexion" + | "legStretchWithBand" + | "legStretchWithLegCircles" + | "lowerLiftOnDisc" + | "lungeSquat" + | "lungesWithKneeLift" + | "mermaidStretch" + | "neutralPelvicPosition" + | "pelvicClocksOnDisc" + | "pilatesPlieSquatsParallelTurnedOutFlatAndHeelsWithChair" + | "piriformisStretch" + | "plankKneeCrosses" + | "plankKneePulls" + | "plankUpDowns" + | "prayerMudra" + | "psoasLungeStretch" + | "ribcageBreathing" + | "rollDown" + | "rollUpWithWeightAndBand" + | "saw" + | "scapularStabilization" + | "scissorsOnDisc" + | "seatedHipStretchup" + | "seatedTwist" + | "shavingTheHeadWithBallBandAndWeight" + | "spinalTwist" + | "spinalTwistStretch" + | "spineStretchForward" + | "squatOpenArmTwistPose" + | "squatsWithBall" + | "standAndHang" + | "standingSideStretch" + | "standingSingleLegForwardBendWithItBandOpener" + | "straightLegCrunchWithLegLift" + | "straightLegCrunchWithLegLiftWithBall" + | "straightLegCrunchWithLegsCrossed" + | "straightLegCrunchWithLegsCrossedWithBall" + | "straightLegDiagonalCrunch" + | "straightLegDiagonalCrunchWithBall" + | "tailboneCurl" + | "throatLock" + | "tickTockSideRoll" + | "twist" + | "vLegCrunches" + | "vSit" + | "forwardFoldWheelchair" + | "forwardFoldPlusWheelchair" + | "armCirclesLowForwardWheelchair" + | "armCirclesMidForwardWheelchair" + | "armCirclesHighForwardWheelchair" + | "armCirclesLowBackwardWheelchair" + | "armCirclesMidBackwardWheelchair" + | "armCirclesHighBackwardWheelchair" + | "coreTwistsWheelchair" + | "armRaiseWheelchair" + | "chestExpandWheelchair" + | "armExtendWheelchair" + | "forwardBendWheelchair" + | "toeTouchWheelchair" + | "extendedToeTouchWheelchair" + | "seatedArmCircles" + | "trunkRotations" + | "seatedTrunkRotations" + | "toeTouch"; + + export type PoseExerciseName = number + | "allFours" + | "ankleToKnee" + | "babyCobra" + | "boat" + | "boundAngle" + | "boundSeatedSingleLegForwardBend" + | "bow" + | "bowedHalfMoon" + | "bridge" + | "cat" + | "chair" + | "childs" + | "corpse" + | "cowFace" + | "cow" + | "devotionalWarrior" + | "dolphinPlank" + | "dolphin" + | "downDogKneeToNose" + | "downDogSplit" + | "downDogSplitOpenHipBentKnee" + | "downwardFacingDog" + | "eagle" + | "easySeated" + | "extendedPuppy" + | "extendedSideAngle" + | "fish" + | "fourLimbedStaff" + | "fullSplit" + | "gate" + | "halfChairHalfAnkleToKnee" + | "halfMoon" + | "headToKnee" + | "heron" + | "heros" + | "highLunge" + | "kneesChestChin" + | "lizard" + | "locust" + | "lowLunge" + | "lowLungeTwist" + | "lowLungeWithKneeDown" + | "mermaid" + | "mountain" + | "oneLeggedDownwardFacingPoseOpenHipBentKnee" + | "oneLeggedPigeon" + | "peacefulWarrior" + | "plank" + | "plow" + | "reclinedHandToFoot" + | "revolvedHalfMoon" + | "revolvedHeadToKnee" + | "revolvedTriangle" + | "runnersLunge" + | "seatedEasySideBend" + | "seatedEasyTwist" + | "seatedLongLegForwardBend" + | "seatedWideLegForwardBend" + | "shoulderStand" + | "sideBoat" + | "sidePlank" + | "sphinx" + | "squatOpenArmTwist" + | "squatPalmPress" + | "staff" + | "standingArmsUp" + | "standingForwardBendHalfwayUp" + | "standingForwardBend" + | "standingSideOpener" + | "standingSingleLegForwardBend" + | "standingSplit" + | "standingWideLegForwardBend" + | "standingWideLegForwardBendWithTwist" + | "supineSpinalTwist" + | "tableTop" + | "threadTheNeedle" + | "thunderbolt" + | "thunderboltPoseBothSidesArmStretch" + | "tree" + | "triangle" + | "upDog" + | "upwardFacingPlank" + | "warriorOne" + | "warriorThree" + | "warriorTwo" + | "wheel" + | "wideSideLunge" + | "deepBreathingWheelchair" + | "deepBreathingLowWheelchair" + | "deepBreathingMidWheelchair" + | "deepBreathingHighWheelchair" + | "prayerWheelchair" + | "overheadPrayerWheelchair" + | "cactusWheelchair" + | "breathingPunchesWheelchair" + | "breathingPunchesExtendedWheelchair" + | "breathingPunchesOverheadWheelchair" + | "breathingPunchesOverheadAndDownWheelchair" + | "breathingPunchesSideWheelchair" + | "breathingPunchesExtendedSideWheelchair" + | "breathingPunchesOverheadSideWheelchair" + | "breathingPunchesOverheadAndDownSideWheelchair" + | "leftHandBackWheelchair" + | "triangleWheelchair" + | "threadTheNeedleWheelchair" + | "neckFlexionAndExtensionWheelchair" + | "neckLateralFlexionWheelchair" + | "spineFlexionAndExtensionWheelchair" + | "spineRotationWheelchair" + | "spineLateralFlexionWheelchair" + | "alternativeSkiingWheelchair" + | "reachForwardWheelchair" + | "warriorWheelchair" + | "reverseWarriorWheelchair" + | "downwardFacingDogToCobra" + | "seatedCatCow"; + + export type TricepsExtensionExerciseName = number + | "benchDip" + | "weightedBenchDip" + | "bodyWeightDip" + | "cableKickback" + | "cableLyingTricepsExtension" + | "cableOverheadTricepsExtension" + | "dumbbellKickback" + | "dumbbellLyingTricepsExtension" + | "ezBarOverheadTricepsExtension" + | "inclineDip" + | "weightedInclineDip" + | "inclineEzBarLyingTricepsExtension" + | "lyingDumbbellPulloverToExtension" + | "lyingEzBarTricepsExtension" + | "lyingTricepsExtensionToCloseGripBenchPress" + | "overheadDumbbellTricepsExtension" + | "recliningTricepsPress" + | "reverseGripPressdown" + | "reverseGripTricepsPressdown" + | "ropePressdown" + | "seatedBarbellOverheadTricepsExtension" + | "seatedDumbbellOverheadTricepsExtension" + | "seatedEzBarOverheadTricepsExtension" + | "seatedSingleArmOverheadDumbbellExtension" + | "singleArmDumbbellOverheadTricepsExtension" + | "singleDumbbellSeatedOverheadTricepsExtension" + | "singleLegBenchDipAndKick" + | "weightedSingleLegBenchDipAndKick" + | "singleLegDip" + | "weightedSingleLegDip" + | "staticLyingTricepsExtension" + | "suspendedDip" + | "weightedSuspendedDip" + | "swissBallDumbbellLyingTricepsExtension" + | "swissBallEzBarLyingTricepsExtension" + | "swissBallEzBarOverheadTricepsExtension" + | "tabletopDip" + | "weightedTabletopDip" + | "tricepsExtensionOnFloor" + | "tricepsPressdown" + | "weightedDip" + | "alternatingDumbbellLyingTricepsExtension" + | "tricepsPress" + | "dumbbellKickbackWheelchair" + | "overheadDumbbellTricepsExtensionWheelchair"; + + export type WarmUpExerciseName = number + | "quadrupedRocking" + | "neckTilts" + | "ankleCircles" + | "ankleDorsiflexionWithBand" + | "ankleInternalRotation" + | "armCircles" + | "bentOverReachToSky" + | "catCamel" + | "elbowToFootLunge" + | "forwardAndBackwardLegSwings" + | "groiners" + | "invertedHamstringStretch" + | "lateralDuckUnder" + | "neckRotations" + | "oppositeArmAndLegBalance" + | "reachRollAndLift" + | "scorpion" /** Deprecated do not use */ + | "shoulderCircles" + | "sideToSideLegSwings" + | "sleeperStretch" + | "slideOut" + | "swissBallHipCrossover" + | "swissBallReachRollAndLift" + | "swissBallWindshieldWipers" + | "thoracicRotation" + | "walkingHighKicks" + | "walkingHighKnees" + | "walkingKneeHugs" + | "walkingLegCradles" + | "walkout" + | "walkoutFromPushUpPosition" + | "bicepsStretch" + | "glutesStretch" + | "standingHamstringStretch" + | "stretch90_90" + | "stretchAbs" + | "stretchButterfly" + | "stretchCalf" + | "stretchCatCow" + | "stretchChildsPose" + | "stretchCobra" + | "stretchForearms" + | "stretchForwardGlutes" + | "stretchFrontSplit" + | "stretchHamstring" + | "stretchHipFlexorAndQuad" + | "stretchLat" + | "stretchLevatorScapulae" + | "stretchLungeWithSpinalTwist" + | "stretchLungingHipFlexor" + | "stretchLyingAbduction" + | "stretchLyingItBand" + | "stretchLyingKneeToChest" + | "stretchLyingPiriformis" + | "stretchLyingSpinalTwist" + | "stretchNeck" + | "stretchObliques" + | "stretchOverUnderShoulder" + | "stretchPectoral" + | "stretchPigeonPose" + | "stretchPiriformis" + | "stretchQuad" + | "stretchScorpion" + | "stretchShoulder" + | "stretchSide" + | "stretchSideLunge" + | "stretchSideSplit" + | "stretchStandingItBand" + | "stretchStraddle" + | "stretchTriceps" + | "stretchWallChestAndShoulder" + | "neckRotationsWheelchair" + | "halfKneelingArmRotation" + | "threeWayAnkleMobilization" + | "ninetyNinetyHipSwitch" /** 90_90_hip_switch */ + | "activeFrog" + | "shoulderSweeps" + | "ankleLunges" + | "backRollFoamRoller" + | "bearCrawl" + | "latissimusDorsiFoamRoll" + | "reverseTHipOpener" + | "shoulderRolls" + | "chestOpeners" + | "tricepsStretch" + | "upperBackStretch" + | "hipCircles" + | "ankleStretch" + | "marchingInPlace" + | "tricepsStretchWheelchair" + | "upperBackStretchWheelchair"; + + export type RunExerciseName = number + | "run" + | "walk" + | "jog" + | "sprint" + | "runOrWalk" + | "speedWalk" + | "warmUp"; + + export type BikeExerciseName = number + | "bike" + | "ride" + | "sprint"; + + export type BandedExercisesExerciseName = number + | "abTwist" + | "backExtension" + | "bicycleCrunch" + | "calfRaises" + | "chestPress" + | "clamShells" + | "curl" + | "deadbug" + | "deadlift" + | "donkeyKick" + | "externalRotation" + | "externalRotationAt90DegreeAbduction" + | "facePull" + | "fireHydrant" + | "fly" + | "frontRaise" + | "gluteBridge" + | "hamstringCurls" + | "highPlankLegLifts" + | "hipExtension" + | "internalRotation" + | "jumpingJack" + | "kneelingCrunch" + | "lateralBandWalks" + | "lateralRaise" + | "latpull" + | "legAbduction" + | "legAdduction" + | "legExtension" + | "lunge" + | "plank" + | "pullApart" + | "pushUps" + | "reverseCrunch" + | "row" + | "shoulderAbduction" + | "shoulderExtension" + | "shoulderExternalRotation" + | "shoulderFlexionTo90Degrees" + | "sidePlankLegLifts" + | "sideRaise" + | "squat" + | "squatToPress" + | "tricepExtension" + | "tricepKickback" + | "uprightRow" + | "wallCrawlWithExternalRotation" + | "lateralRaiseWheelchair" + | "tricepsExtensionWheelchair" + | "chestFlyInclineWheelchair" + | "chestFlyDeclineWheelchair" + | "pullDownWheelchair" + | "straightArmPullDownWheelchair" + | "curlWheelchair" + | "overheadCurlWheelchair" + | "facePullWheelchair" + | "aroundTheWorldWheelchair" + | "pullApartWheelchair" + | "sideCurlWheelchair" + | "overheadPressWheelchair"; + + export type BattleRopeExerciseName = number + | "alternatingFigureEight" + | "alternatingJumpWave" + | "alternatingKneelingToStandingWave" + | "alternatingLungeWave" + | "alternatingSquatWave" + | "alternatingWave" + | "alternatingWaveWithLateralShuffle" + | "clapWave" + | "doubleArmFigureEight" + | "doubleArmSideToSideSnake" + | "doubleArmSideWave" + | "doubleArmSlam" + | "doubleArmWave" + | "grapplerToss" + | "hipToss" + | "inAndOutWave" + | "insideCircle" + | "jumpingJacks" + | "outsideCircle" + | "rainbow" + | "sidePlankWave" + | "sidewinder" + | "sittingRussianTwist" + | "snakeWave" + | "splitJack" + | "stageCoach" + | "ultimateWarrior" + | "upperCuts"; + + export type EllipticalExerciseName = number + | "elliptical"; + + export type FloorClimbExerciseName = number + | "floorClimb"; + + export type IndoorBikeExerciseName = number + | "airBike" + | "assaultBike" + | "stationaryBike"; + + export type IndoorRowExerciseName = number + | "rowingMachine"; + + export type LadderExerciseName = number + | "agility" + | "speed"; + + export type SandbagExerciseName = number + | "aroundTheWorld" + | "backSquat" + | "bearCrawlPullThrough" + | "bearHugSquat" + | "clean" + | "cleanAndPress" + | "curl" + | "frontCarry" + | "frontSquat" + | "lunge" + | "overheadPress" + | "plankPullThrough" + | "rotationalLunge" + | "row" + | "russianTwist" + | "shouldering" + | "shoveling" + | "sideLunge" + | "sprint" + | "zercherSquat"; + + export type SledExerciseName = number + | "backwardDrag" + | "chestPress" + | "forwardDrag" + | "lowPush" + | "push" + | "row"; + + export type SledgeHammerExerciseName = number + | "lateralSwing" + | "hammerSlam"; + + export type StairStepperExerciseName = number + | "stairStepper"; + + export type SuspensionExerciseName = number + | "chestFly" + | "chestPress" + | "crunch" + | "curl" + | "dip" + | "facePull" + | "gluteBridge" + | "hamstringCurl" + | "hipDrop" + | "invertedRow" + | "kneeDriveJump" + | "kneeToChest" + | "latPullover" + | "lunge" + | "mountainClimber" + | "pendulum" + | "pike" + | "plank" + | "powerPull" + | "pullUp" + | "pushUp" + | "reverseMountainClimber" + | "reversePlank" + | "rollout" + | "row" + | "sideLunge" + | "sidePlank" + | "singleLegDeadlift" + | "singleLegSquat" + | "sitUp" + | "split" + | "squat" + | "squatJump" + | "tricepPress" + | "yFly"; + + export type TireExerciseName = number + | "flip"; + + export type BikeOutdoorExerciseName = number + | "bike"; + + export type RunIndoorExerciseName = number + | "indoorTrackRun" + | "treadmill"; + + export type WaterType = number + | "fresh" + | "salt" + | "en13319" + | "custom"; + + export type TissueModelType = number + | "zhl16c" /** Buhlmann's decompression algorithm, version C */; + + export type DiveGasStatus = number + | "disabled" + | "enabled" + | "backupOnly"; + + export type DiveAlert = number + | "ndlReached" + | "gasSwitchPrompted" + | "nearSurface" + | "approachingNdl" + | "po2Warn" + | "po2CritHigh" + | "po2CritLow" + | "timeAlert" + | "depthAlert" + | "decoCeilingBroken" + | "decoComplete" + | "safetyStopBroken" + | "safetyStopComplete" + | "cnsWarning" + | "cnsCritical" + | "otuWarning" + | "otuCritical" + | "ascentCritical" + | "alertDismissedByKey" + | "alertDismissedByTimeout" + | "batteryLow" + | "batteryCritical" + | "safetyStopStarted" + | "approachingFirstDecoStop" + | "setpointSwitchAutoLow" + | "setpointSwitchAutoHigh" + | "setpointSwitchManualLow" + | "setpointSwitchManualHigh" + | "autoSetpointSwitchIgnored" + | "switchedToOpenCircuit" + | "switchedToClosedCircuit" + | "tankBatteryLow" + | "po2CcrDilLow" /** ccr diluent has low po2 */ + | "decoStopCleared" /** a deco stop has been cleared */ + | "apneaNeutralBuoyancy" /** Target Depth Apnea Alarm triggered */ + | "apneaTargetDepth" /** Neutral Buoyance Apnea Alarm triggered */ + | "apneaSurface" /** Surface Apnea Alarm triggered */ + | "apneaHighSpeed" /** High Speed Apnea Alarm triggered */ + | "apneaLowSpeed" /** Low Speed Apnea Alarm triggered */; + + export type DiveAlarmType = number + | "depth" /** Alarm when a certain depth is crossed */ + | "time" /** Alarm when a certain time has transpired */ + | "speed" /** Alarm when a certain ascent or descent rate is exceeded */; + + export type DiveBacklightMode = number + | "atDepth" + | "alwaysOn"; + + export type SleepLevel = number + | "unmeasurable" + | "awake" + | "light" + | "deep" + | "rem"; + + export type Spo2MeasurementType = number + | "offWrist" + | "spotCheck" + | "continuousCheck" + | "periodic"; + + export type CcrSetpointSwitchMode = number + | "manual" /** User switches setpoints manually */ + | "automatic" /** Switch automatically based on depth */; + + export type DiveGasMode = number + | "openCircuit" + | "closedCircuitDiluent"; + + export type ProjectileType = number + | "arrow" /** Arrow projectile type */ + | "rifleCartridge" /** Rifle cartridge projectile type */ + | "pistolCartridge" /** Pistol cartridge projectile type */ + | "shotshell" /** Shotshell projectile type */ + | "airRiflePellet" /** Air rifle pellet projectile type */ + | "other" /** Other projectile type */; + + export type FaveroProduct = number + | "assiomaUno" + | "assiomaDuo"; + + export type SplitType = number + | "ascentSplit" + | "descentSplit" + | "intervalActive" + | "intervalRest" + | "intervalWarmup" + | "intervalCooldown" + | "intervalRecovery" + | "intervalOther" + | "climbActive" + | "climbRest" + | "surfActive" + | "runActive" + | "runRest" + | "workoutRound" + | "rwdRun" /** run/walk detection running */ + | "rwdWalk" /** run/walk detection walking */ + | "windsurfActive" + | "rwdStand" /** run/walk detection standing */ + | "transition" /** Marks the time going from ascent_split to descent_split/used in backcountry ski */ + | "skiLiftSplit" + | "skiRunSplit"; + + export type ClimbProEvent = number + | "approach" + | "start" + | "complete"; + + export type GasConsumptionRateType = number + | "pressureSac" /** Pressure-based Surface Air Consumption */ + | "volumeSac" /** Volumetric Surface Air Consumption */ + | "rmv" /** Respiratory Minute Volume */; + + export type TapSensitivity = number + | "high" + | "medium" + | "low"; + + export type RadarThreatLevelType = number + | "threatUnknown" + | "threatNone" + | "threatApproaching" + | "threatApproachingFast"; + + export type SleepDisruptionSeverity = number + | "none" + | "low" + | "medium" + | "high"; + + export type NapPeriodFeedback = number + | "none" + | "multipleNapsDuringDay" + | "jetlagIdealTimingIdealDuration" + | "jetlagIdealTimingLongDuration" + | "jetlagLateTimingIdealDuration" + | "jetlagLateTimingLongDuration" + | "idealTimingIdealDurationLowNeed" + | "idealTimingIdealDurationHighNeed" + | "idealTimingLongDurationLowNeed" + | "idealTimingLongDurationHighNeed" + | "lateTimingIdealDurationLowNeed" + | "lateTimingIdealDurationHighNeed" + | "lateTimingLongDurationLowNeed" + | "lateTimingLongDurationHighNeed" + | "idealDurationLowNeed" + | "idealDurationHighNeed" + | "longDurationLowNeed" + | "longDurationHighNeed"; + + export type NapSource = number + | "automatic" + | "manualDevice" + | "manualGc"; + + export type MaxMetSpeedSource = number + | "onboardGps" + | "connectedGps" + | "cadence"; + + export type MaxMetHeartRateSource = number + | "whr" /** Wrist Heart Rate Monitor */ + | "hrm" /** Chest Strap Heart Rate Monitor */; + + export type HrvStatus = number + | "none" + | "poor" + | "low" + | "unbalanced" + | "balanced"; + + export type NoFlyTimeMode = number + | "standard" /** Standard Diver Alert Network no-fly guidance */ + | "flat24Hours" /** Flat 24 hour no-fly guidance */; +} + +export default Types; diff --git a/src/types/utils.d.ts b/src/types/utils.d.ts new file mode 100644 index 0000000..538da16 --- /dev/null +++ b/src/types/utils.d.ts @@ -0,0 +1,55 @@ +///////////////////////////////////////////////////////////////////////////////////////////// +// Copyright 2026 Garmin International, Inc. +// Licensed under the Flexible and Interoperable Data Transfer (FIT) Protocol License; you +// may not use this file except in compliance with the Flexible and Interoperable Data +// Transfer (FIT) Protocol License. +///////////////////////////////////////////////////////////////////////////////////////////// +// ****WARNING**** This file is auto-generated! Do NOT edit this file. +// Profile Version = 21.205.0Release +// Tag = production/release/21.205.0-0-gb3c261eb +///////////////////////////////////////////////////////////////////////////////////////////// + + +export declare const Utils: { + /** Millisecond offset between the UNIX epoch (1970-01-01) and the FIT epoch (1989-12-31): `631065600000`. */ + readonly FIT_EPOCH_MS: number; + + /** + * Converts a FIT DateTime value to a JavaScript `Date`. + * @param datetime - Seconds since the FIT epoch (1989-12-31 00:00:00 UTC). + * @returns The equivalent JavaScript `Date`. + */ + convertDateTimeToDate(datetime: number): Date; + + /** + * Converts a JavaScript `Date` to a FIT DateTime value. + * @param date - The JavaScript `Date` to convert. + * @returns Seconds since the FIT epoch (1989-12-31 00:00:00 UTC). + */ + convertDateToDateTime(date: Date): number; + + /** FIT base type numeric constants. */ + readonly FitBaseType: { + readonly ENUM: number; + readonly SINT8: number; + readonly UINT8: number; + readonly SINT16: number; + readonly UINT16: number; + readonly SINT32: number; + readonly UINT32: number; + readonly STRING: number; + readonly FLOAT32: number; + readonly FLOAT64: number; + readonly UINT8Z: number; + readonly UINT16Z: number; + readonly UINT32Z: number; + readonly BYTE: number; + readonly SINT64: number; + readonly UINT64: number; + readonly UINT64Z: number; + }; + /** Maps a FIT base type number to its string field-type name (e.g. `3` → `"uint8"`). */ + readonly BaseTypeToFieldType: Record; + /** Maps a FIT field-type name to its base type number (e.g. `"uint8"` → `3`). */ + readonly FieldTypeToBaseType: Record; +}; diff --git a/src/utils-hr-mesg.js b/src/utils-hr-mesg.js index 4b192b1..2f0965b 100644 --- a/src/utils-hr-mesg.js +++ b/src/utils-hr-mesg.js @@ -5,8 +5,8 @@ // Transfer (FIT) Protocol License. ///////////////////////////////////////////////////////////////////////////////////////////// // ****WARNING**** This file is auto-generated! Do NOT edit this file. -// Profile Version = 21.202.0Release -// Tag = production/release/21.202.0-0-g9a57aebc +// Profile Version = 21.205.0Release +// Tag = production/release/21.205.0-0-gb3c261eb ///////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/utils-internal.js b/src/utils-internal.js index 646ec6d..1435862 100644 --- a/src/utils-internal.js +++ b/src/utils-internal.js @@ -5,17 +5,25 @@ // Transfer (FIT) Protocol License. ///////////////////////////////////////////////////////////////////////////////////////////// // ****WARNING**** This file is auto-generated! Do NOT edit this file. -// Profile Version = 21.202.0Release -// Tag = production/release/21.202.0-0-g9a57aebc +// Profile Version = 21.205.0Release +// Tag = production/release/21.205.0-0-gb3c261eb ///////////////////////////////////////////////////////////////////////////////////////////// -const sanitizeValues = (values) => { +const sanitizeValues = (values, { legacyArrayMode = false, isArray = false } = {}) => { if (onlyNullValues(values)) { return null; } - return values.length === 1 ? values[0] : values; + if (legacyArrayMode) { + return values.length === 1 ? values[0] : values; + } + + if (isArray) { + return values; + } + + return values[0]; } const trimStringTrailingNulls = (string) => { @@ -35,11 +43,11 @@ const trimStringTrailingNulls = (string) => { return strings.length === 1 ? strings[0] : strings; } -const onlyNullValues = (values) => values.reduce((state, value) => value != null ? false : state, true); +const onlyNullValues = (values) => values.every(value => value == null); const onlyInvalidValues = (rawFieldValue, invalidValue) => { if (Array.isArray(rawFieldValue)) { - return rawFieldValue.reduce((state, value) => value != invalidValue ? false : state, true); + return rawFieldValue.every(value => value === invalidValue); } return rawFieldValue === invalidValue; diff --git a/src/utils-memo-glob.js b/src/utils-memo-glob.js index 951cc02..06cb8ae 100644 --- a/src/utils-memo-glob.js +++ b/src/utils-memo-glob.js @@ -5,8 +5,8 @@ // Transfer (FIT) Protocol License. ///////////////////////////////////////////////////////////////////////////////////////////// // ****WARNING**** This file is auto-generated! Do NOT edit this file. -// Profile Version = 21.202.0Release -// Tag = production/release/21.202.0-0-g9a57aebc +// Profile Version = 21.205.0Release +// Tag = production/release/21.205.0-0-gb3c261eb ///////////////////////////////////////////////////////////////////////////////////////////// import Profile from "./profile.js"; diff --git a/src/utils.js b/src/utils.js index 252eebe..27ce426 100644 --- a/src/utils.js +++ b/src/utils.js @@ -5,33 +5,19 @@ // Transfer (FIT) Protocol License. ///////////////////////////////////////////////////////////////////////////////////////////// // ****WARNING**** This file is auto-generated! Do NOT edit this file. -// Profile Version = 21.202.0Release -// Tag = production/release/21.202.0-0-g9a57aebc +// Profile Version = 21.205.0Release +// Tag = production/release/21.205.0-0-gb3c261eb ///////////////////////////////////////////////////////////////////////////////////////////// import FIT from "./fit.js"; -/** - * The millisecond offset between UNIX and FIT Epochs (631065600000). - * @const {number} - */ const FIT_EPOCH_MS = 631065600000; -/** - * Convert a FIT DateTime to a JavaScript Date - * @param {number} datetime - Seconds since FIT EPOCH - * @returns {Date} A JavaScript Date object - */ const convertDateTimeToDate = (datetime) => { return new Date((datetime ?? 0) * 1000 + FIT_EPOCH_MS); }; -/** - * Convert a JavaScript Date to a FIT DateTime - * @param {Date} A JavaScript Date object - * @return {number} datetime - Seconds since FIT EPOCH - */ const convertDateToDateTime = (date) => { return (date.getTime() - FIT_EPOCH_MS) / 1000; }; diff --git a/test/bit-stream.test.js b/test/bit-stream.test.js index 8f62873..617263c 100644 --- a/test/bit-stream.test.js +++ b/test/bit-stream.test.js @@ -11,143 +11,145 @@ import BitStream from "../src/bit-stream.js"; import FIT from "../src/fit.js"; describe("Bit Stream Tests", () => { - describe("From Byte Array Tests", () => { - test("Next Bit", () => { - const bitStream = new BitStream([0xAA, 0xAA]); - const values = [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1]; - values.forEach((expected, index) => { - expect(bitStream.bitsAvailable === values.length - index); - expect(bitStream.hasBitsAvailable); - - const actual = bitStream.readBit(); - expect(actual).toBe(expected); - - expect(bitStream.bitsAvailable === values.length - index - 1); - expect(bitStream.hasBitsAvailable === (values.length === index + 1)); + describe("Read Bit Tests", () => { + test("readBit from byte array reads each bit LSB first", () => { + const bitStream = new BitStream([0xAA, 0xFF], FIT.BaseType.UINT8); + const expected = [0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1]; + expected.forEach((exp, index) => { + expect(bitStream.bitsAvailable).toBe(expected.length - index); + expect(bitStream.hasBitsAvailable).toBe(true); + expect(bitStream.readBit()).toBe(exp); + expect(bitStream.bitsAvailable).toBe(expected.length - index - 1); }); }); - const parameters = [ - { - data: [0xAA], - baseType: FIT.BaseType.UINT8, - nBitsToRead: [4, 4], - values: [0xA, 0xA] - }, - { - data: [0xAA], - baseType: FIT.BaseType.UINT8, - nBitsToRead: [8], - values: [0xAA] - }, - { - data: [0xAA, 0xAA], - baseType: FIT.BaseType.UINT8, - nBitsToRead: [16], - values: [0xAAAA] - }, - { - data: [0xFF, 0xFF], - baseType: FIT.BaseType.UINT8, - nBitsToRead: [16], - values: [0xFFFF] - }, - { - data: [0xAA, 0xAA, 0xAA, 0x2A], - baseType: FIT.BaseType.UINT8, - nBitsToRead: [32], - values: [0x2AAAAAAA] - }, - - { - data: [0x10, 0x32, 0x54, 0x76], - baseType: FIT.BaseType.UINT8, - nBitsToRead: [32], - values: [0x76543210] - }, - ] - test.each(parameters)( - "Test %s", - (scenario) => { - const bitStream = new BitStream(scenario.data, scenario.baseType); - scenario.values.forEach((expected, index) => { - const actual = bitStream.readBits(scenario.nBitsToRead[index]); - expect(actual).toBe(expected); - }); + test("readBit from integer reads each bit LSB first", () => { + const bitStream = new BitStream(0xAAFF, FIT.BaseType.UINT16); + const expected = [1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1]; + expected.forEach((exp, index) => { + expect(bitStream.bitsAvailable).toBe(expected.length - index); + expect(bitStream.hasBitsAvailable).toBe(true); + expect(bitStream.readBit()).toBe(exp); + expect(bitStream.bitsAvailable).toBe(expected.length - index - 1); }); + }); }); - describe("From Integer Tests", () => { - test("Next Bit", () => { - const bitStream = new BitStream(0x0FAA, FIT.BaseType.UINT16); - const values = [0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0]; - values.forEach((expected) => { - const actual = bitStream.readBit(); - expect(actual).toBe(expected); - }); + describe("Read Bits From Array Tests", () => { + const parameters = [ + { label: "UInt8 [0xAB] - 8", data: [0xAB], baseType: FIT.BaseType.UINT8, nBitsToRead: [8], values: [0xAB], takesFastPath: true }, + { label: "UInt8 [0xAB] - 4,4", data: [0xAB], baseType: FIT.BaseType.UINT8, nBitsToRead: [4, 4], values: [0xB, 0xA], takesFastPath: false }, + { label: "UInt8 [0xAB] - 4,1,1,1,1", data: [0xAB], baseType: FIT.BaseType.UINT8, nBitsToRead: [4, 1, 1, 1, 1], values: [0xB, 0, 1, 0, 1], takesFastPath: false }, + { label: "UInt8 [0xAA, 0xCB] - 16 (cross-boundary)", data: [0xAA, 0xCB], baseType: FIT.BaseType.UINT8, nBitsToRead: [16], values: [0xCBAA], takesFastPath: false }, + { label: "UInt8 [0xAA, 0xCB, 0xDE, 0xFF] - 16,16 (cross-boundary)", data: [0xAA, 0xCB, 0xDE, 0xFF], baseType: FIT.BaseType.UINT8, nBitsToRead: [16, 16], values: [0xCBAA, 0xFFDE], takesFastPath: false }, + { label: "UInt8 [0xAA, 0xCB, 0xDE, 0xFF] - 32 (cross-boundary)", data: [0xAA, 0xCB, 0xDE, 0xFF], baseType: FIT.BaseType.UINT8, nBitsToRead: [32], values: [0xFFDECBAA], takesFastPath: false }, + { label: "UInt8 [0xAA, 0xBB] - 8,8", data: [0xAA, 0xBB], baseType: FIT.BaseType.UINT8, nBitsToRead: [8, 8], values: [0xAA, 0xBB], takesFastPath: true }, + { label: "UInt16 [0xABCD, 0xEF01] - 16,16", data: [0xABCD, 0xEF01], baseType: FIT.BaseType.UINT16, nBitsToRead: [16, 16], values: [0xABCD, 0xEF01], takesFastPath: true }, + { label: "UInt16 [0xABCD, 0xEF01] - 32", data: [0xABCD, 0xEF01], baseType: FIT.BaseType.UINT16, nBitsToRead: [32], values: [0xEF01ABCD], takesFastPath: false }, + { label: "UInt32 [0xABCDEF01] - 32", data: [0xABCDEF01], baseType: FIT.BaseType.UINT32, nBitsToRead: [32], values: [0xABCDEF01], takesFastPath: true }, + { label: "UInt32 [0xABCDEF01, 0x12345678] - 32,32", data: [0xABCDEF01, 0x12345678], baseType: FIT.BaseType.UINT32, nBitsToRead: [32, 32], values: [0xABCDEF01, 0x12345678], takesFastPath: true }, + { label: "UInt64 [0x7BCDEF0123456789] - 64", data: [0x7BCDEF0123456789n], baseType: FIT.BaseType.UINT64, nBitsToRead: [64], values: [Number(0x7BCDEF0123456789n)], takesFastPath: true }, + { label: "UInt64 two elements - 64,64", data: [0x7BCDEF0123456789n, 0x0BCDEF0123456789n], baseType: FIT.BaseType.UINT64, nBitsToRead: [64, 64], values: [Number(0x7BCDEF0123456789n), Number(0x0BCDEF0123456789n)], takesFastPath: true }, + { label: "UInt64 [0xABCDEF0123456789] - 32 (lower half)", data: [0xABCDEF0123456789n], baseType: FIT.BaseType.UINT64, nBitsToRead: [32], values: [0x23456789], takesFastPath: false }, + { label: "UInt64 [0xABCDEF0123456789] - 32,32", data: [0xABCDEF0123456789n], baseType: FIT.BaseType.UINT64, nBitsToRead: [32, 32], values: [0x23456789, 0xABCDEF01], takesFastPath: false }, + ]; + test.each(parameters)("$label", (scenario) => { + const bitStream = new BitStream(scenario.data, scenario.baseType); + scenario.values.forEach((expected, index) => { + expect(bitStream.readBits(scenario.nBitsToRead[index])).toBe(expected); + }); + expect(bitStream.array === null).toBe(scenario.takesFastPath); }); + }); + describe("Read Bits From Integer Tests", () => { const parameters = [ - { - data: 0xAA, - baseType: FIT.BaseType.UINT8, - nBitsToRead: [4], - values: [0xA] - }, - { - data: 0xAA, - baseType: FIT.BaseType.UINT8, - nBitsToRead: [4, 4], - values: [0xA, 0xA] - }, - { - data: 0xAA, - baseType: FIT.BaseType.UINT8, - nBitsToRead: [4, 1, 1, 1, 1], - values: [0xA, 0x0, 0x1, 0x0, 0x1] - }, - { - data: 0xAA, - baseType: FIT.BaseType.UINT16, - nBitsToRead: [4, 1, 1, 1, 1], - values: [0xA, 0x0, 0x1, 0x0, 0x1] - }, - { - data: [0xAAAA, 0x2AAA], - baseType: FIT.BaseType.UINT16, - nBitsToRead: [32], - values: [0x2AAAAAAA] - }, - { - data: [0xAAAAAAAA], - baseType: FIT.BaseType.UINT32, - nBitsToRead: [16, 8, 8], - values: [0xAAAA, 0xAA, 0xAA] - }, + { label: "UInt8 0xAB - 8", data: 0xAB, baseType: FIT.BaseType.UINT8, nBitsToRead: [8], values: [0xAB], takesFastPath: true }, + { label: "UInt8 0xAB - 4,4", data: 0xAB, baseType: FIT.BaseType.UINT8, nBitsToRead: [4, 4], values: [0xB, 0xA], takesFastPath: false }, + { label: "UInt8 0xAB - 4,1,1,1,1", data: 0xAB, baseType: FIT.BaseType.UINT8, nBitsToRead: [4, 1, 1, 1, 1], values: [0xB, 0, 1, 0, 1], takesFastPath: false }, + { label: "UInt16 0xAACB - 16", data: 0xAACB, baseType: FIT.BaseType.UINT16, nBitsToRead: [16], values: [0xAACB], takesFastPath: true }, + { label: "UInt32 0xABCDEF01 - 16,16", data: 0xABCDEF01, baseType: FIT.BaseType.UINT32, nBitsToRead: [16, 16], values: [0xEF01, 0xABCD], takesFastPath: false }, + { label: "UInt32 0xABCDEF01 - 32", data: 0xABCDEF01, baseType: FIT.BaseType.UINT32, nBitsToRead: [32], values: [0xABCDEF01], takesFastPath: true }, + { label: "UInt64 0x7BCDEF0123456789 - 64", data: 0x7BCDEF0123456789n, baseType: FIT.BaseType.UINT64, nBitsToRead: [64], values: [Number(0x7BCDEF0123456789n)], takesFastPath: true }, + { label: "UInt64 0xABCDEF0123456789 - 64", data: 0xABCDEF0123456789n, baseType: FIT.BaseType.UINT64, nBitsToRead: [64], values: [Number(0xABCDEF0123456789n)], takesFastPath: true }, + { label: "UInt64 0xABCDEF0123456789 - 32 (lower half)", data: 0xABCDEF0123456789n, baseType: FIT.BaseType.UINT64, nBitsToRead: [32], values: [0x23456789], takesFastPath: false }, + { label: "UInt64 0xABCDEF0123456789 - 32,32", data: 0xABCDEF0123456789n, baseType: FIT.BaseType.UINT64, nBitsToRead: [32, 32], values: [0x23456789, 0xABCDEF01], takesFastPath: false }, + { label: "SInt8 25 - 8 positive value unchanged by mask", data: 25, baseType: FIT.BaseType.SINT8, nBitsToRead: [8], values: [25], takesFastPath: true }, + { label: "SInt8 -56 (0xC8) - 8 unsigned bit pattern", data: -56, baseType: FIT.BaseType.SINT8, nBitsToRead: [8], values: [200], takesFastPath: true }, + { label: "SInt16 500 - 16 positive value unchanged by mask", data: 500, baseType: FIT.BaseType.SINT16, nBitsToRead: [16], values: [500], takesFastPath: true }, + { label: "SInt16 -1 (0xFFFF) - 16 unsigned bit pattern", data: -1, baseType: FIT.BaseType.SINT16, nBitsToRead: [16], values: [0xFFFF], takesFastPath: true }, + { label: "Float32 0x3FC00000 (1.5f) - 32 bit mask", data: 0x3FC00000, baseType: FIT.BaseType.FLOAT32, nBitsToRead: [32], values: [0x3FC00000], takesFastPath: true }, + { label: "Float64 0x3FF8000000000000 (1.5d) - 64 bit mask", data: 0x3FF8000000000000n, baseType: FIT.BaseType.FLOAT64, nBitsToRead: [64], values: [Number(0x3FF8000000000000n)], takesFastPath: true }, ]; - test.each(parameters)( - "Test %s", - (scenario) => { - const bitStream = new BitStream(scenario.data, scenario.baseType); - scenario.values.forEach((expected, index) => { - const actual = bitStream.readBits(scenario.nBitsToRead[index]); - expect(actual).toBe(expected); - }); + test.each(parameters)("$label", (scenario) => { + const bitStream = new BitStream(scenario.data, scenario.baseType); + scenario.values.forEach((expected, index) => { + expect(bitStream.readBits(scenario.nBitsToRead[index])).toBe(expected); }); + expect(bitStream.array === null).toBe(scenario.takesFastPath); + }); }); - describe("Should Thrown When Reading More Bits Than Available Tests", () => { - test("When reading more bits than available readBit() should throw", () => { - const bitStream = new BitStream(0xAAAA, FIT.BaseType.UINT16); - bitStream.readBits(16); + describe("Exception Tests", () => { + test("readBits throws when no bits available", () => { + const bitStream = new BitStream(0xABCDEFFF, FIT.BaseType.UINT32); + bitStream.readBits(32); + expect(() => { bitStream.readBits(2) }).toThrowError("FIT Runtime Error"); + }); + + test("readBit throws when no bits available", () => { + const bitStream = new BitStream(0xAB, FIT.BaseType.UINT8); + bitStream.readBits(8); expect(() => { bitStream.readBit() }).toThrowError("FIT Runtime Error"); }); + }); + + describe("Deferred Init Tests", () => { + test("fast path fires for each full-element read leaving array null between reads", () => { + const bitStream = new BitStream([0x11, 0x22, 0x33], FIT.BaseType.UINT8); + expect(bitStream.array).toBeNull(); + expect(bitStream.readBits(8)).toBe(0x11); + expect(bitStream.array).toBeNull(); + expect(bitStream.bitsAvailable).toBe(16); + expect(bitStream.readBits(8)).toBe(0x22); + expect(bitStream.array).toBeNull(); + expect(bitStream.readBits(8)).toBe(0x33); + expect(bitStream.bitsAvailable).toBe(0); + }); + + test("partial read after fast-path consumption initialises array at correct offset", () => { + const bitStream = new BitStream([0xAB, 0xCD], FIT.BaseType.UINT8); + expect(bitStream.readBits(8)).toBe(0xAB); + expect(bitStream.array).toBeNull(); + expect(bitStream.readBits(4)).toBe(0xD); + expect(bitStream.array).not.toBeNull(); + expect(bitStream.readBits(4)).toBe(0xC); + expect(bitStream.bitsAvailable).toBe(0); + }); + + test("readBit after fast-path consumption initialises array lazily", () => { + const bitStream = new BitStream([0xFF, 0x00], FIT.BaseType.UINT8); + expect(bitStream.readBits(8)).toBe(0xFF); + expect(bitStream.array).toBeNull(); + expect(bitStream.readBit()).toBe(0); + expect(bitStream.array).not.toBeNull(); + for (let i = 0; i < 7; i++) { + expect(bitStream.readBit()).toBe(0); + } + expect(bitStream.bitsAvailable).toBe(0); + }); - test("When reading more bits than available readBits() should throw", () => { - const bitStream = new BitStream(0xAAAA, FIT.BaseType.UINT16); - expect(() => { bitStream.readBits(32) }).toThrowError("FIT Runtime Error"); + test("reset restores array to null making fast path available again", () => { + const bitStream = new BitStream([0x12, 0x34], FIT.BaseType.UINT8); + bitStream.readBits(4); + expect(bitStream.array).not.toBeNull(); + bitStream.reset(); + expect(bitStream.array).toBeNull(); + expect(bitStream.bitsAvailable).toBe(16); + expect(bitStream.readBits(8)).toBe(0x12); + expect(bitStream.readBits(8)).toBe(0x34); }); }); }); diff --git a/test/data/test-data.js b/test/data/test-data.js index 1d4f13c..ab4df3a 100644 --- a/test/data/test-data.js +++ b/test/data/test-data.js @@ -139,6 +139,18 @@ const fitFileCompressedSpeedAndDistanceWithInitialDistance = [ 0x0D, 0x00, 0x8B, 0x00, 0x08, 0x00, 0xF9, 0x00, 0x14, 0x65, 0xB1 ]; +const fitFileComponentFieldWithTarget = [ + 0x0E, 0x02, 0xD0, 0x52, 0x13, 0x00, 0x00, 0x00, 0x2E, 0x46, 0x49, 0x54, + 0x9C, 0x2E, 0x40, 0x00, 0x00, 0x14, 0x00, 0x02, 0x02, 0x02, 0x84, 0x4E, + 0x04, 0x86, 0x00, 0xDE, 0x21, 0x29, 0x5E, 0x00, 0x00, 0x45, 0xE9 +]; + +const fitFileUndefinedFieldArray = [ + 0x0E, 0x20, 0xCA, 0x52, 0x0C, 0x00, 0x00, 0x00, 0x2E, 0x46, 0x49, 0x54, + 0x5C, 0xAF, 0x40, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, + 0x04, 0x03, 0x7B, 0x3B +]; + const fitFileMemoGlobSimple = [ 0x0E, 0x20, 0xAB, 0x52, 0x45, 0x00, 0x00, 0x00, 0x2E, 0x46, 0x49, 0x54, 0xCA, 0x8E, 0x40, 0x00, 0x01, 0x00, 0x1B, 0x02, 0xFE, 0x02, 0x84, 0x08, @@ -692,11 +704,13 @@ export default { fitFileAccumulatedComponents, fitFileCompressedSpeedAndDistanceWithInitialDistance, fitFileCompressedSpeedAndDistance, + fitFileComponentFieldWithTarget, fitFileMemoGlobSimple, fitFileMemoGlobMultibyte, fitFileMemoGlobLoremIpsum, fitFileMemoGlobUnknownTargetField, fitFileMemoGlobUnknownTargetMesg, + fitFileUndefinedFieldArray, gearChangeData, workout800mRepeatsLittleEndian, workout800mRepeatsBigEndian diff --git a/test/decoder.test.js b/test/decoder.test.js index c7e297d..bdc9cdb 100644 --- a/test/decoder.test.js +++ b/test/decoder.test.js @@ -11,9 +11,12 @@ import * as fs from "fs"; import CRC from "../src/crc-calculator.js"; import Decoder from "../src/decoder.js"; +import FIT from "../src/fit.js"; +import Profile from "../src/profile.js"; import Stream from "../src/stream.js"; import Data from "./data/test-data.js"; import HrData from "./data/test-data-expand-hr-mesgs.js"; +import { uint16LE, uint32LE, uint64LE, buildFit } from "./utils/fit-builder.js"; describe("Decoder Tests", () => { describe("Decoder Constructor Tests", () => { @@ -503,6 +506,14 @@ describe("Decoder Tests", () => { expect(messages.recordMesgs[1].enhancedSpeed).toBe(2.49) }); + test("Component Expansion into existing field should not create an array", () => { + const stream = Stream.fromByteArray(Data.fitFileComponentFieldWithTarget); + const decode = new Decoder(stream); + const { messages, errors } = decode.read(); + expect(errors.length).toBe(0); + expect(messages.recordMesgs[0].altitude).toBe(messages.recordMesgs[0].enhancedAltitude); + }); + }); describe("Sub-Field Expansion Tests", () => { @@ -726,4 +737,112 @@ describe("Decoder Tests", () => { expect(errors.length).toBe(0); }); }); + + describe("Decode Legacy Array Mode", () => { + test.each([ + { description: "legacyArrayMode: true allows arrays for non-array fields", options: { legacyArrayMode: true }, isArray: true, expected: ["activity", "sport"] }, + { description: "legacyArrayMode: false enforces profile arrays", options: { legacyArrayMode: false }, isArray: false, expected: "activity" }, + { description: "legacyArrayMode defaults to false", options: undefined, isArray: false, expected: "activity" }, + ])("$description", ({ options, isArray, expected }) => { + const stream = Stream.fromByteArray(Data.fitFileUndefinedFieldArray); + const decoder = new Decoder(stream); + const { messages, errors } = decoder.read(options); + + expect(errors.length).toBe(0); + expect(Array.isArray(messages.fileIdMesgs[0].type)).toBe(isArray); + if (isArray) { + expect(messages.fileIdMesgs[0].type).toEqual(expected); + } else { + expect(messages.fileIdMesgs[0].type).toBe(expected); + } + }); + }); +}); + +const RECORD = Profile.MesgNum.RECORD; +const MONITORING = Profile.MesgNum.MONITORING; +const UINT8 = FIT.BaseType.UINT8; +const UINT16 = FIT.BaseType.UINT16; +const UINT32 = FIT.BaseType.UINT32; +const UINT64 = FIT.BaseType.UINT64; +const BYTE = FIT.BaseType.BYTE; + +const ALTITUDE_FIELD_NUM = Profile.messages[Profile.MesgNum.RECORD].fields[2].num; +const SPEED_FIELD_NUM = Profile.messages[Profile.MesgNum.RECORD].fields[6].num; +const CYCLES_RECORD_FIELD_NUM = Profile.messages[Profile.MesgNum.RECORD].fields[18].num; +const COMPRESSED_SPEED_DISTANCE_FIELD_NUM = Profile.messages[Profile.MesgNum.RECORD].fields[8].num; +const CURRENT_ACTIVITY_TYPE_INTENSITY_FIELD_NUM = Profile.messages[Profile.MesgNum.MONITORING].fields[24].num; +const CYCLES_MONITORING_FIELD_NUM = Profile.messages[Profile.MesgNum.MONITORING].fields[3].num; + +describe("Component Expansion By Base Type Tests", () => { + const parameters = [ + { + label: "UINT16 speed -> UINT32 enhanced_speed, scale only", + mesgNum: RECORD, fieldDefs: [[SPEED_FIELD_NUM, 2, UINT16]], recordData: uint16LE(1390), + mesgKey: "recordMesgs", expected: { speed: 1.39, enhancedSpeed: 1.39 }, + }, + { + label: "UINT16 speed -> UINT32 enhanced_speed, integer result", + mesgNum: RECORD, fieldDefs: [[SPEED_FIELD_NUM, 2, UINT16]], recordData: uint16LE(5000), + mesgKey: "recordMesgs", expected: { speed: 5.0, enhancedSpeed: 5 }, + }, + { + label: "UINT16 altitude -> UINT32 enhanced_altitude, positive", + mesgNum: RECORD, fieldDefs: [[ALTITUDE_FIELD_NUM, 2, UINT16]], recordData: uint16LE(3000), + mesgKey: "recordMesgs", expected: { altitude: 100.0, enhancedAltitude: 100 }, + }, + { + label: "UINT16 altitude -> UINT32 enhanced_altitude, zero", + mesgNum: RECORD, fieldDefs: [[ALTITUDE_FIELD_NUM, 2, UINT16]], recordData: uint16LE(2500), + mesgKey: "recordMesgs", expected: { altitude: 0.0, enhancedAltitude: 0 }, + }, + { + label: "UINT16 altitude -> UINT32 enhanced_altitude, negative", + mesgNum: RECORD, fieldDefs: [[ALTITUDE_FIELD_NUM, 2, UINT16]], recordData: uint16LE(2000), + mesgKey: "recordMesgs", expected: { altitude: -100.0, enhancedAltitude: -100 }, + }, + { + label: "UINT8 cycles -> UINT32 total_cycles, accumulated", + mesgNum: RECORD, fieldDefs: [[CYCLES_RECORD_FIELD_NUM, 1, UINT8]], recordData: [100], + mesgKey: "recordMesgs", expected: { cycles: 100, totalCycles: 100 }, + }, + { + label: "UINT8 cycles -> UINT32 total_cycles, max non-invalid", + mesgNum: RECORD, fieldDefs: [[CYCLES_RECORD_FIELD_NUM, 1, UINT8]], recordData: [254], + mesgKey: "recordMesgs", expected: { cycles: 254, totalCycles: 254 }, + }, + { + label: "BYTE[3] compressed_speed_distance -> multi-component cascade", + mesgNum: RECORD, fieldDefs: [[COMPRESSED_SPEED_DISTANCE_FIELD_NUM, 3, BYTE]], recordData: [0x8B, 0x00, 0x08], + mesgKey: "recordMesgs", expected: { speed: 1.39, distance: 8, enhancedSpeed: 1.39 }, + }, + { + label: "BYTE -> enum activity_type + UINT8 intensity, multi-component", + mesgNum: MONITORING, fieldDefs: [[CURRENT_ACTIVITY_TYPE_INTENSITY_FIELD_NUM, 1, BYTE], [CYCLES_MONITORING_FIELD_NUM, 4, UINT32]], + recordData: [0x61, ...uint32LE(20)], + mesgKey: "monitoringMesgs", expected: { activityType: "running", intensity: 3, cycles: 10.0 }, + }, + { + label: "BYTE -> enum activity_type=walking, intensity=0", + mesgNum: MONITORING, fieldDefs: [[CURRENT_ACTIVITY_TYPE_INTENSITY_FIELD_NUM, 1, BYTE], [CYCLES_MONITORING_FIELD_NUM, 4, UINT32]], + recordData: [0x06, ...uint32LE(30)], + mesgKey: "monitoringMesgs", expected: { activityType: "walking", intensity: 0, cycles: 15.0 }, + }, + ]; + + test.each(parameters)("$label", (scenario) => { + const data = buildFit(scenario.mesgNum, scenario.fieldDefs, [scenario.recordData]); + const stream = Stream.fromByteArray(data); + const decoder = new Decoder(stream); + const { messages, errors } = decoder.read({ mergeHeartRates: false }); + expect(errors.length).toBe(0); + const msg = messages[scenario.mesgKey][0]; + for (const [key, val] of Object.entries(scenario.expected)) { + if (typeof val === "number" && !Number.isInteger(val)) { + expect(msg[key]).toBeCloseTo(val); + } else { + expect(msg[key]).toBe(val); + } + } + }); }); diff --git a/test/encoder.test.js b/test/encoder.test.js index 040502e..2f556fa 100644 --- a/test/encoder.test.js +++ b/test/encoder.test.js @@ -471,208 +471,278 @@ describe("Encoder-Decoder Integration Tests", () => { }); describe("Base Type Encode-Decode Tests", () => { - test.for([ - ["uint8", 123, 123], - ["uint16", 12345, 12345], - ["uint32", 1234567890, 1234567890], - ["sint8", -123, -123], - ["sint16", -12345, -12345], - ["sint32", -123456789, -123456789], - ["string", "Test String", "Test String"], - ["float32", 123.4, 123.4], - ["float64", 123456.789012, 123456.789012], - ["uint8z", 200, 200], - ["uint16z", 60000, 60000], - ["uint32z", 4000000000, 4000000000], - ["byte", 0xDE, 0xDE], - ["sint64", -12345678901234n, -12345678901234n], - ["uint64", 12345678901234n, 12345678901234n], - ["uint64z", 12345678901234n, 12345678901234n], - // Array Tests - ["uint8", [12, 34, 56], [12, 34, 56]], - ["uint16", [12345, 54321], [12345, 54321]], - ["uint32", [1234567890, 987654321], [1234567890, 987654321]], - ["sint8", [-123, -12], [-123, -12]], - ["sint16", [-12345, -5432], [-12345, -5432]], - ["sint32", [-123456789, -98765432], [-123456789, -98765432]], - ["string", ["Test String 1", "Test String 2"], ["Test String 1", "Test String 2"]], - ["float32", [123.4, 432.1], [123.4, 432.1]], - ["float64", [123456.789012, 210987.654321], [123456.789012, 210987.654321]], - ["uint8z", [200, 150], [200, 150]], - ["uint16z", [60000, 30000], [60000, 30000]], - ["uint32z", [4000000000, 2000000000], [4000000000, 2000000000]], - ["byte", [0xDE, 0xAD], [0xDE, 0xAD]], - ["sint64", [-12345678901234n, -43210987654321n], [-12345678901234n, -43210987654321n]], - ["uint64", [12345678901234n, 43210987654321n], [12345678901234n, 43210987654321n]], - ["uint64z", [12345678901234n, 43210987654321n], [12345678901234n, 43210987654321n]], - // Offset Tests - ["uint8", 123, 123, { offset: 2 }], - ["uint16", 12345, 12345, { offset: 2 }], - ["uint32", 1234567890, 1234567890, { offset: 2 }], - ["sint8", -123, -123, { offset: 2 }], - ["sint16", -12345, -12345, { offset: 2 }], - ["sint32", -123456789, -123456789, { offset: 2 }], - ["string", "Test String", "Test String", { offset: 2 }], - ["float32", 123.4, 123.4, { offset: 2 }], - ["float64", 123456.789012, 123456.789012, { offset: 2 }], - ["uint8z", 200, 200, { offset: 2 }], - ["uint16z", 60000, 60000, { offset: 2 }], - ["uint32z", 4000000000, 4000000000, { offset: 2 }], - ["byte", 0xDE, 0xDE, { offset: 2 }], - // Scale Tests - ["uint8", 123, 123, { scale: 2 }], - ["uint16", 12345, 12345, { scale: 2 }], - ["uint32", 1234567890, 1234567890, { scale: 2 }], - ["sint8", -12, -12, { scale: 2 }], - ["sint16", -1234, -1234, { scale: 2 }], - ["sint32", -12345, -12345, { scale: 2 }], - ["string", "Test String", "Test String", { scale: 2 }], - ["float32", 123.4, 123.4, { scale: 2 }], - ["float64", 123456.789012, 123456.789012, { scale: 2 }], - ["uint8z", 123, 123, { scale: 2 }], - ["uint16z", 1234, 1234, { scale: 2 }], - ["uint32z", 12345, 12345, { scale: 2 }], - ["byte", 0x01, 0x01, { scale: 2 }], - // 64 bit Scale/Offset Tests (Decoder Scale/Offset not applied) - ["sint64", -100n, -98n, { offset: 2 }], - ["uint64", 100n, 102n, { offset: 2 }], - ["uint64z", 100n, 102n, { offset: 2 }], - ["sint64", -500n, -1000n, { scale: 2 }], - ["uint64", 100n, 200n, { scale: 2 }], - ["uint64z", 100n, 200n, { scale: 2 }], - ["uint64", 123.45, 12345n, { scale: 100 }], - // Integer Fields Scale Rounding Tests - ["uint8", 12.21, 12.2, { scale: 10 }], - ["uint8", 12.77, 12.8, { scale: 10 }], - ["uint8", 12.5, 12.5, { scale: 10 }], - // String Numeric Value Tests - ["uint8", "123", 123], - ["float32", "123.456", 123.456], - ["float64", "123456.789012", 123456.789012], - ["uint64", "12345678901234", 12345678901234n], - ["sint64", "-12345678901234", -12345678901234n], - ["uint64", "1234567890123456789012345678901234567890", 12446928571455179474n], - // Overflow Mask Tests - ["uint8", 0x1234, 0x34], - ["sint8", 0x12FF, -1], - ["uint8z", 0x1234, 0x34], - ["uint16", 0x123456, 0x3456], - ["sint16", 0x12FFFF, -1], - ["uint16z", 0x123456, 0x3456], - ["uint32", 0x1234567899, 0x34567899], - ["sint32", 0x12FFFFFFFF, -1], - ["uint32z", 0x1234567899, 0x34567899], - ["byte", 0x1234, 0x34], - ["uint64", 0x12FFFFFFFFFFFFFFFFFFFFFFFFFn, 0xFFFFFFFFFFFFFFFFn], - ["uint64z", 0x12FFFFFFFFFFFFFFFFFFFFFFFFFFn, 0xFFFFFFFFFFFFFFFFn], - ["sint64", 0x12FFFFFFFFFFFFFFFFFFFFFFFFFFFFn, -1n], - ])("Encoding field of base type: %s %#", ([fitBaseType, value, expectedValue, { scale = 1, offset = 0 } = {}]) => { - - addCustomMesgToFitProfile(DEFAULT_CUSTOM_MESG_NUM, "testMesg", { - 0: { name: "testField", type: fitBaseType, baseType: fitBaseType, scale, offset, }, - }) - - const testMesg = { - testField: value, - } + const arrayTestData = [ + { baseType: "uint8", values: [12, 34, 56] }, + { baseType: "uint16", values: [12345, 54321] }, + { baseType: "uint32", values: [1234567890, 987654321] }, + { baseType: "sint8", values: [-123, -12] }, + { baseType: "sint16", values: [-12345, -5432] }, + { baseType: "sint32", values: [-123456789, -98765432] }, + { baseType: "float32", values: [123.4, 432.1] }, + { baseType: "float64", values: [123456.789012, 210987.654321] }, + { baseType: "uint8z", values: [200, 150] }, + { baseType: "uint16z", values: [60000, 30000] }, + { baseType: "uint32z", values: [4000000000, 2000000000] }, + { baseType: "byte", values: [0xDE, 0xAD] }, + { baseType: "byte", values: [0xFF, 0xAB, 0xFF] }, + { baseType: "sint64", values: [-12345678901234n, -43210987654321n] }, + { baseType: "uint64", values: [12345678901234n, 43210987654321n] }, + { baseType: "uint64z", values: [12345678901234n, 43210987654321n] }, + ]; - const { messages, errors, } = encodeThenDecodeMesgs([{ mesgNum: DEFAULT_CUSTOM_MESG_NUM, mesg: testMesg }]); + const stringFieldTestData = [ + { baseType: "string", values: ["Test String 1", "Test String 2"], fieldProperties: { array: true } }, + { baseType: "string", values: ["Test String 1", "Test String 2"], fieldProperties: { array: false } }, + { baseType: "string", values: ["Test String 1"], fieldProperties: { array: false } }, + { baseType: "string", values: "Test String 1", fieldProperties: { array: false } }, + ]; - expect(errors.length).toBe(0); - const mesg = messages.testMesgMesgs[0]; + const scaleTestData = [ + { baseType: "uint8", values: 123, fieldProperties: { scale: 2 } }, + { baseType: "uint16", values: 12345, fieldProperties: { scale: 2 } }, + { baseType: "uint32", values: 1234567890, fieldProperties: { scale: 2 } }, + { baseType: "sint8", values: -12, fieldProperties: { scale: 2 } }, + { baseType: "sint16", values: -12345, fieldProperties: { scale: 2 } }, + { baseType: "sint32", values: -123456789, fieldProperties: { scale: 2 } }, + { baseType: "float32", values: 123.4, fieldProperties: { scale: 2 } }, + { baseType: "float64", values: 123456.789012, fieldProperties: { scale: 2 } }, + ]; + + const offsetTestData = [ + { baseType: "uint8", values: 123, fieldProperties: { offset: 2 } }, + { baseType: "uint16", values: 12345, fieldProperties: { offset: 2 } }, + { baseType: "uint32", values: 1234567890, fieldProperties: { offset: 2 } }, + { baseType: "sint8", values: -123, fieldProperties: { offset: 2 } }, + { baseType: "sint16", values: -12345, fieldProperties: { offset: 2 } }, + { baseType: "sint32", values: -123456789, fieldProperties: { offset: 2 } }, + { baseType: "string", values: "Test String", fieldProperties: { offset: 2 } }, + { baseType: "float32", values: 123.4, fieldProperties: { offset: 2 } }, + { baseType: "float64", values: 123456.789012, fieldProperties: { offset: 2 } }, + { baseType: "uint8z", values: 200, fieldProperties: { offset: 2 } }, + { baseType: "uint16z", values: 60000, fieldProperties: { offset: 2 } }, + { baseType: "uint32z", values: 4000000000, fieldProperties: { offset: 2 } }, + { baseType: "byte", values: 0xDE, fieldProperties: { offset: 2 } }, + ]; - expectValuesEqualGivenBaseType(fitBaseType, expectedValue, mesg.testField); + const scaleOffset64bitTestData = [ + { baseType: "sint64", values: -100n, offset: 2, expectedValue: -98n }, + { baseType: "uint64", values: 100n, offset: 2, expectedValue: 102n }, + { baseType: "uint64z", values: 100n, offset: 2, expectedValue: 102n }, + { baseType: "sint64", values: -500n, scale: 2, expectedValue: -1000n }, + { baseType: "uint64", values: 100n, scale: 2, expectedValue: 200n }, + { baseType: "uint64z", values: 100n, scale: 2, expectedValue: 200n }, + { baseType: "uint64", values: 123.45, scale: 100, expectedValue: 12345n }, + ]; + + const stringNumericValueTestData = [ + { baseType: "uint8", value: "123", expectedValue: 123 }, + { baseType: "float32", value: "123.456", expectedValue: 123.456 }, + { baseType: "float64", value: "123456.789012", expectedValue: 123456.789012 }, + { baseType: "uint64", value: "12345678901234", expectedValue: 12345678901234n }, + { baseType: "sint64", value: "-12345678901234", expectedValue: -12345678901234n }, + { baseType: "uint64", value: "1234567890123456789012345678901234567890", expectedValue: 12446928571455179474n }, + ]; + + const overflowMaskTestData = [ + { baseType: "uint8", value: 0x1234, expectedValue: 0x34 }, + { baseType: "sint8", value: 0x12FF, expectedValue: -1 }, + { baseType: "uint8z", value: 0x1234, expectedValue: 0x34 }, + { baseType: "uint16", value: 0x123456, expectedValue: 0x3456 }, + { baseType: "sint16", value: 0x12FFFF, expectedValue: -1 }, + { baseType: "uint16z", value: 0x123456, expectedValue: 0x3456 }, + { baseType: "uint32", value: 0x1234567899, expectedValue: 0x34567899 }, + { baseType: "sint32", value: 0x12FFFFFFFF, expectedValue: -1 }, + { baseType: "uint32z", value: 0x1234567899, expectedValue: 0x34567899 }, + { baseType: "byte", value: 0x1234, expectedValue: 0x34 }, + { baseType: "uint64", value: 0x12FFFFFFFFFFFFFFFFFFFFFFFFFn, expectedValue: 0xFFFFFFFFFFFFFFFFn }, + { baseType: "uint64z", value: 0x12FFFFFFFFFFFFFFFFFFFFFFFFFFn, expectedValue: 0xFFFFFFFFFFFFFFFFn }, + { baseType: "sint64", value: 0x12FFFFFFFFFFFFFFFFFFFFFFFFFFFFn, expectedValue: -1n }, + ]; + + const developerFieldTestData = [ + { baseType: "uint8", values: 123, }, + { baseType: "uint16", values: 12345, }, + { baseType: "uint32", values: 1234567890, }, + { baseType: "sint8", values: -123, }, + { baseType: "sint16", values: -12345, }, + { baseType: "sint32", values: -123456789, }, + { baseType: "string", values: "Test String", }, + { baseType: "float32", values: 123.456, }, + { baseType: "float64", values: 123456.789012, }, + { baseType: "uint8z", values: 200, }, + { baseType: "uint16z", values: 60000, }, + { baseType: "uint32z", values: 4000000000, }, + { baseType: "byte", values: 0xDE, }, + { baseType: "sint64", values: -12345678901234n, }, + { baseType: "uint64", values: 12345678901234n, }, + { baseType: "uint64z", values: 12345678901234n, }, + { baseType: "uint8", values: [12, 34, 56], fieldProperties: { array: true } }, + { baseType: "uint16", values: [12345, 54321], fieldProperties: { array: true } }, + { baseType: "uint32", values: [1234567890, 987654321], fieldProperties: { array: true } }, + { baseType: "sint8", values: [-123, -12], fieldProperties: { array: true } }, + { baseType: "sint16", values: [-12345, -5432], fieldProperties: { array: true } }, + { baseType: "sint32", values: [-123456789, -98765432], fieldProperties: { array: true } }, + { baseType: "float32", values: [123.4, 432.1], fieldProperties: { array: true } }, + { baseType: "float64", values: [123456.789012, 210987.654321], fieldProperties: { array: true } }, + { baseType: "uint8z", values: [200, 150], fieldProperties: { array: true } }, + { baseType: "uint16z", values: [60000, 30000], fieldProperties: { array: true } }, + { baseType: "uint32z", values: [4000000000, 2000000000], fieldProperties: { array: true } }, + { baseType: "byte", values: [0xDE, 0xAD], fieldProperties: { array: true } }, + { baseType: "sint64", values: [-12345678901234n, -43210987654321n], fieldProperties: { array: true } }, + { baseType: "uint64", values: [12345678901234n, 43210987654321n], fieldProperties: { array: true } }, + { baseType: "uint64z", values: [12345678901234n, 43210987654321n], fieldProperties: { array: true } }, + { baseType: "string", values: ["Test String 1", "Test String 2"], fieldProperties: { array: true } }, + { baseType: "string", values: ["Test String 1", "Test String 2"], fieldProperties: { array: false } }, + ]; + + // MARK: LegacyArrayMode Tests + describe("LegacyArrayMode Tests", () => { + test.for([ + { baseType: "string", values: ["Test String 1", "Test String 2"], fieldProperties: { array: true } }, + { baseType: "string", values: ["Test String 1", "Test String 2"], fieldProperties: { array: false } }, + ...arrayTestData.map(({ baseType, values }) => ({ baseType, values, fieldProperties: { array: true } })), + ...arrayTestData.map(({ baseType, values }) => ({ baseType, values, fieldProperties: { array: false } })), + ])("Legacy Encode-Decode $baseType when array is $fieldProperties.array", ({ baseType, values, fieldProperties = {} }) => { + const { messages, errors } = encodeDecodeFieldMesg(baseType, values, fieldProperties, { legacyArrayMode: true }); + expect(errors.length).toBe(0); + expectValuesEqualGivenBaseType(baseType, values, messages.testMesgMesgs[0].testField); + }); + + test.for(stringFieldTestData)("Legacy Encode-Decode string field", ({ baseType, values, fieldProperties = {} }) => { + const { messages, errors } = encodeDecodeFieldMesg(baseType, values, fieldProperties, { legacyArrayMode: true }); + expect(errors.length).toBe(0); + expect(messages.testMesgMesgs[0].testField).toEqual(values?.length === 1 ? values[0] : values); + }); + + test.for(developerFieldTestData)("Legacy Encode-Decode developer field with base type $baseType array: $fieldProperties.array", ({ baseType, values, fieldProperties = {} }) => { + const { errors, actualValue } = encodeDecodeDevField(baseType, values, fieldProperties, { legacyArrayMode: false }); + expect(errors.length).toBe(0); + expectValuesEqualGivenBaseType(baseType, values, actualValue); + }); + + test.for([ + { baseType: "string", values: ["Test String 1", "Test String 2"], fieldProperties: { array: true } }, + { baseType: "string", values: ["Test String 1", "Test String 2"], fieldProperties: { array: false } }, + { baseType: "string", values: "Test String 1", fieldProperties: { array: false } }, + { baseType: "uint8", values: [12, 34, 56], fieldProperties: { array: true } }, + { baseType: "uint8", values: [12, 34, 56], fieldProperties: { array: false } }, + ])("Legacy Encode-Decode developer field does not truncate $baseType non-profile arrays", ({ baseType, values, fieldProperties = {} }) => { + const { errors, actualValue } = encodeDecodeDevField(baseType, values, fieldProperties, { legacyArrayMode: true }); + expect(errors.length).toBe(0); + expectValuesEqualGivenBaseType(baseType, values, actualValue); + }); }); - test.for([ - ["uint8", 123], - ["uint8", [12, 34, 56]], - ["uint16", 12345], - ["uint32", 1234567890], - ["sint8", -123], - ["sint16", -12345], - ["sint32", -123456789], - ["string", "Test String"], - ["float32", 123.456], - ["float64", 123456.789012], - ["uint8z", 200], - ["uint16z", 60000], - ["uint32z", 4000000000], - ["byte", 0xDE], - ["sint64", -12345678901234n], - ["uint64", 12345678901234n], - ["uint64z", 12345678901234n], - // Array Tests - ["uint8", [12, 34, 56]], - ["uint16", [12345, 54321]], - ["uint32", [1234567890, 987654321]], - ["sint8", [-123, -12]], - ["sint16", [-12345, -5432]], - ["sint32", [-123456789, -98765432]], - ["string", ["Test String 1", "Test String 2"]], - ["float32", [123.4, 432.1]], - ["float64", [123456.789012, 210987.654321]], - ["uint8z", [200, 150]], - ["uint16z", [60000, 30000]], - ["uint32z", [4000000000, 2000000000]], - ["byte", [0xDE, 0xAD]], - ["sint64", [-12345678901234n, -43210987654321n]], - ["uint64", [12345678901234n, 43210987654321n]], - ["uint64z", [12345678901234n, 43210987654321n]], - ])("Encoding developer field of base type: %s", ([fitBaseType, expectedValue]) => { - const DEV_FIELD_KEY = 0; + // MARK: String Tests + test.for(stringFieldTestData)("Encode-Decode string field", ({ baseType, values, fieldProperties = {} }) => { + const { messages, errors } = encodeDecodeFieldMesg(baseType, values, fieldProperties); + expect(errors.length).toBe(0); + expect(messages.testMesgMesgs[0].testField).toBe(Array.isArray(values) ? values[0] : values); + }); - const developerDataIdMesg = { - applicationId: Array(16).fill(0), - applicationVersion: 1, - developerDataIndex: 0, - }; + // MARK: Array Tests + test.for([ + ...arrayTestData.map(({ baseType, values }) => ({ baseType, values, fieldProperties: { array: true } })), + ...arrayTestData.map(({ baseType, values }) => ({ baseType, values, fieldProperties: { array: false } })), + ])("Encode-Decode $baseType when array is $fieldProperties.array", ({ baseType, values, fieldProperties = {} }) => { + const { messages, errors } = encodeDecodeFieldMesg(baseType, values, fieldProperties); + expect(errors.length).toBe(0); + const expectedValues = fieldProperties.array ? values : values[0]; + expectValuesEqualGivenBaseType(baseType, expectedValues, messages.testMesgMesgs[0].testField); + }); - const fieldDescriptionMesg = { - developerDataIndex: 0, - fieldDefinitionNumber: 0, - fitBaseTypeId: Utils.FieldTypeToBaseType[fitBaseType], - fieldName: "Test Field", - units: "units", - nativeMesgNum: Profile.MesgNum.SESSION, - }; + // MARK: Scale Tests + test.for(scaleTestData)("Encode-Decode $baseType with scale", ({ baseType, values, fieldProperties }) => { + const { messages, errors } = encodeDecodeFieldMesg(baseType, values, fieldProperties); + expect(errors.length).toBe(0); + expectValuesEqualGivenBaseType(baseType, values, messages.testMesgMesgs[0].testField); + }); - const fieldDescriptions = { - [DEV_FIELD_KEY]: { - developerDataIdMesg, - fieldDescriptionMesg, - }, - }; + // MARK: Offset Tests + test.for(offsetTestData)("Encode-Decode $baseType with offset", ({ baseType, values, fieldProperties = {} }) => { + const { messages, errors } = encodeDecodeFieldMesg(baseType, values, fieldProperties); + expect(errors.length).toBe(0); + expectValuesEqualGivenBaseType(baseType, values, messages.testMesgMesgs[0].testField); + }); - const sessionMesg = { - messageIndex: 0, - sport: "running", - developerFields: { - [DEV_FIELD_KEY]: expectedValue, - }, - } + // MARK: 64 bit Scale/Offset Tests (Decoder Scale/Offset not applied) + test.for(scaleOffset64bitTestData)("Encode-Decode 64bit type $baseType does not apply scale/offset", ({ baseType, values, scale, offset, expectedValue }) => { + const { messages, errors } = encodeDecodeFieldMesg(baseType, values, { scale, offset }); + expect(errors.length).toBe(0); + expectValuesEqualGivenBaseType(baseType, expectedValue, messages.testMesgMesgs[0].testField); + }); - const mesgs = [ - { mesgNum: Profile.MesgNum.DEVELOPER_DATA_ID, mesg: developerDataIdMesg, }, - { mesgNum: Profile.MesgNum.FIELD_DESCRIPTION, mesg: fieldDescriptionMesg, }, - { mesgNum: Profile.MesgNum.SESSION, mesg: sessionMesg, }, - ]; + // MARK: Integer Fields Scale Rounding Tests + test.for([ + { baseType: "uint8", value: 12.21, scale: 10, expectedValue: 12.2 }, + { baseType: "uint8", value: 12.77, scale: 10, expectedValue: 12.8 }, + { baseType: "uint8", value: 12.5, scale: 10, expectedValue: 12.5 }, + ])("Encode-Decode integer $baseType with scale rounds to nearest value", ({ baseType, value, scale, expectedValue }) => { + const { messages, errors } = encodeDecodeFieldMesg(baseType, value, { scale }); + expect(errors.length).toBe(0); + expectValuesEqualGivenBaseType(baseType, expectedValue, messages.testMesgMesgs[0].testField); + }); - const { messages, errors, } = encodeThenDecodeMesgs(mesgs, { fieldDescriptions, }); + // MARK: String Numeric Value Tests + test.for(stringNumericValueTestData)("Encode-Decode $baseType with string numeric value", ({ baseType, value, expectedValue }) => { + const { messages, errors } = encodeDecodeFieldMesg(baseType, value); + expect(errors.length).toBe(0); + expectValuesEqualGivenBaseType(baseType, expectedValue, messages.testMesgMesgs[0].testField); + }); + // MARK: Overflow Mask Tests + test.for(overflowMaskTestData)("Encode-Decode $baseType with overflow value applies overflow mask", ({ baseType, value, expectedValue }) => { + const { messages, errors } = encodeDecodeFieldMesg(baseType, value); expect(errors.length).toBe(0); - expect(messages.sessionMesgs.length).toBe(1); + expectValuesEqualGivenBaseType(baseType, expectedValue, messages.testMesgMesgs[0].testField); + }); + + // MARK: Developer Field Tests + describe("Developer Field Tests", () => { + test.for(developerFieldTestData)("Encode-Decode developer field with base type $baseType array: $fieldProperties.array", ({ baseType, values, fieldProperties = {} }) => { + const { errors, actualValue } = encodeDecodeDevField(baseType, values, fieldProperties); + expect(errors.length).toBe(0); + expectValuesEqualGivenBaseType(baseType, values, actualValue); + }); + + test.for(arrayTestData)("Encode-Decode developer field $baseType non-profile arrays are not truncated", ({ baseType, values, fieldProperties = {} }) => { + const { errors, actualValue } = encodeDecodeDevField(baseType, values, fieldProperties); + expect(errors.length).toBe(0); + expectValuesEqualGivenBaseType(baseType, values, actualValue); + }); - const actualValue = messages.sessionMesgs[0].developerFields[DEV_FIELD_KEY]; + test.for([ + { baseType: "string", values: ["Test String 1", "Test String 2"] }, + ...arrayTestData, + ])("LegacyArrayMode - Encoding developer array field of base type: $baseType preserves array", ({ baseType, values, fieldProperties = {} }) => { + const { errors, actualValue } = encodeDecodeDevField(baseType, values, fieldProperties, { legacyArrayMode: true }); + expect(errors.length).toBe(0); + expectValuesEqualGivenBaseType(baseType, values, actualValue); + }); - expectValuesEqualGivenBaseType(fitBaseType, expectedValue, actualValue); + test.for([ + { baseType: "string", values: ["Test String 1", "Test String 2"] }, + ...arrayTestData, + ])("Encoding developer array field of base type: $baseType preserves array", ({ baseType, values, fieldProperties = {} }) => { + const { errors, actualValue } = encodeDecodeDevField(baseType, values, fieldProperties); + expect(errors.length).toBe(0); + expectValuesEqualGivenBaseType(baseType, values, actualValue); + }); }); }); }); const expectValuesEqualGivenBaseType = (fitBaseType, expectedValues, actualValues) => { - expectedValues = Array.isArray(expectedValues) ? expectedValues : [expectedValues]; - actualValues = Array.isArray(actualValues) ? actualValues : [actualValues]; + const isExpectedArray = Array.isArray(expectedValues); + + expect(Array.isArray(actualValues)).toBe(isExpectedArray); + + if (!isExpectedArray) { + (fitBaseType === "string" || typeof expectedValues === "bigint") ? + expect(actualValues).toEqual(expectedValues) : + expect(actualValues).toBeCloseTo(expectedValues, 2); + return; + } expect(actualValues.length).toBe(expectedValues.length); @@ -685,4 +755,41 @@ const expectValuesEqualGivenBaseType = (fitBaseType, expectedValues, actualValue }); }; +const encodeDecodeFieldMesg = (baseType, value, fieldProperties = {}, { legacyArrayMode = false } = {}) => { + addCustomMesgToFitProfile(DEFAULT_CUSTOM_MESG_NUM, "testMesg", { + 0: { name: "testField", type: baseType, baseType, ...fieldProperties, }, + }); + const { messages, errors, } = encodeThenDecodeMesgs( + [{ mesgNum: DEFAULT_CUSTOM_MESG_NUM, mesg: { testField: value } }], + { decoderOptions: { legacyArrayMode } }, + ); + return { messages, errors }; +}; + +const encodeDecodeDevField = (baseType, value, fieldProperties = {}, { legacyArrayMode = false } = {}) => { + const DEV_FIELD_KEY = 0; + const developerDataIdMesg = { + applicationId: Array(16).fill(0), + applicationVersion: 1, + developerDataIndex: 0, + }; + const fieldDescriptionMesg = { + developerDataIndex: 0, + fieldDefinitionNumber: 0, + fitBaseTypeId: Utils.FieldTypeToBaseType[baseType], + fieldName: "Test Field", + units: "units", + nativeMesgNum: Profile.MesgNum.SESSION, + array: Number(fieldProperties.array || false), + }; + const fieldDescriptions = { [DEV_FIELD_KEY]: { developerDataIdMesg, fieldDescriptionMesg } }; + const mesgs = [ + { mesgNum: Profile.MesgNum.DEVELOPER_DATA_ID, mesg: developerDataIdMesg, }, + { mesgNum: Profile.MesgNum.FIELD_DESCRIPTION, mesg: fieldDescriptionMesg, }, + { mesgNum: Profile.MesgNum.SESSION, mesg: { messageIndex: 0, sport: "running", developerFields: { [DEV_FIELD_KEY]: value } }, }, + ]; + const { messages, errors, } = encodeThenDecodeMesgs(mesgs, { fieldDescriptions, decoderOptions: { legacyArrayMode } }); + return { messages, errors, actualValue: messages.sessionMesgs?.[0]?.developerFields?.[DEV_FIELD_KEY] }; +}; + diff --git a/test/stream.test.js b/test/stream.test.js index 2660ab2..53b32ed 100644 --- a/test/stream.test.js +++ b/test/stream.test.js @@ -53,7 +53,7 @@ describe("Stream Tests", () => { test("Read UInt8 Array", () => { const stream = Stream.fromByteArray([0x01, 0x02, 0x03]); - const values = stream.readValue(FIT.BaseType.UINT8, 3); + const values = stream.readValue(FIT.BaseType.UINT8, 3, { isArray: true }); expect(values.length).toBe(3); expect(Array.from(new Uint8Array(values))).toEqual([1, 2, 3]); }); @@ -88,7 +88,7 @@ describe("Stream Tests", () => { ...[0x02, 0x00], ...[0x03, 0x00] ]); - const values = stream.readValue(FIT.BaseType.UINT16, 6); + const values = stream.readValue(FIT.BaseType.UINT16, 6, { isArray: true }); expect(values.length).toBe(3); expect(Array.from(new Uint16Array(values))).toEqual([1, 2, 3]); }); @@ -124,7 +124,7 @@ describe("Stream Tests", () => { ...[0x02, 0x00, 0x00, 0x00], ...[0x03, 0x00, 0x00, 0x00] ]); - const values = stream.readValue(FIT.BaseType.UINT32, 12); + const values = stream.readValue(FIT.BaseType.UINT32, 12, { isArray: true }); expect(values.length).toBe(3); expect(Array.from(new Uint32Array(values))).toEqual([1, 2, 3]); }); @@ -160,7 +160,7 @@ describe("Stream Tests", () => { ...[0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], ...[0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] ]); - const values = stream.readValue(FIT.BaseType.UINT64, 24); + const values = stream.readValue(FIT.BaseType.UINT64, 24, { isArray: true }); expect(values.length).toBe(3); expect(Array.from(new BigUint64Array(values))).toEqual([1n, 2n, 3n]); }); @@ -196,7 +196,7 @@ describe("Stream Tests", () => { ...[0x00, 0x00, 0x00, 0x40], ...[0x00, 0x00, 0x40, 0x40] ]); - const values = stream.readValue(FIT.BaseType.FLOAT32, 12); + const values = stream.readValue(FIT.BaseType.FLOAT32, 12, { isArray: true }); expect(values.length).toBe(3); expect(Array.from(new Float32Array(values))).toEqual([1.0, 2.0, 3.0]); }); @@ -219,15 +219,15 @@ describe("Stream Tests", () => { ...[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40], ...[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x40] ]); - const values = stream.readValue(FIT.BaseType.FLOAT64, 24); + const values = stream.readValue(FIT.BaseType.FLOAT64, 24, { isArray: true }); expect(values.length).toBe(3); expect([1.0, 2.0, 3.0]).toEqual(Array.from(new Float64Array(values))); }); test("Test Bytes Read", () => { const stream = Stream.fromByteArray([0xF]); - stream.readValue(FIT.BaseType.BYTE, 1) - const value = stream.bytesRead + stream.readValue(FIT.BaseType.BYTE, 1); + const value = stream.bytesRead; expect(value).toBe(1); }); @@ -238,7 +238,7 @@ describe("Stream Tests", () => { ]; test.each(byteArrays)("Test Byte Array with Invalids", (byteArray) => { const stream = Stream.fromByteArray(byteArray.data); - const values = stream.readValue(FIT.BaseType.BYTE, byteArray.data.length); + const values = stream.readValue(FIT.BaseType.BYTE, byteArray.data.length, { isArray: true }); expect(values).toEqual(byteArray.expected); }); @@ -254,13 +254,13 @@ describe("Stream Tests", () => { describe("Big Endian Tests", () => { test("Read UInt16", () => { const stream = Stream.fromByteArray([0x01, 0x02].reverse()); - const value = stream.readUInt16({ endianness: Stream.BIG_ENDIAN }); + const value = stream.readUInt16({ littleEndian: false }); expect(value).toBe(0x0201); }); test("Read Int16", () => { const stream = Stream.fromByteArray([0xFE, 0xFF].reverse()); - const value = stream.readInt16({ endianness: Stream.BIG_ENDIAN }); + const value = stream.readInt16({ littleEndian: false }); expect(value).toBe(-2); }); @@ -270,20 +270,20 @@ describe("Stream Tests", () => { ...[0x02, 0x00].reverse(), ...[0x03, 0x00].reverse() ]); - const values = stream.readValue(FIT.BaseType.UINT16, 6, { endianness: Stream.BIG_ENDIAN }); + const values = stream.readValue(FIT.BaseType.UINT16, 6, { littleEndian: false, isArray: true }); expect(values.length).toBe(3); expect(Array.from(new Uint16Array(values))).toEqual([1, 2, 3]); }); test("Read UInt32", () => { const stream = Stream.fromByteArray([0x01, 0x02, 0x03, 0x04].reverse()); - const value = stream.readUInt32({ endianness: Stream.BIG_ENDIAN }); + const value = stream.readUInt32({ littleEndian: false }); expect(value).toBe(0x04030201); }); test("Read Int32", () => { const stream = Stream.fromByteArray([0xFF, 0xFF, 0xFF, 0xFF].reverse()); - const value = stream.readInt32({ endianness: Stream.BIG_ENDIAN }); + const value = stream.readInt32({ littleEndian: false }); expect(value).toBe(-1); }); @@ -293,20 +293,20 @@ describe("Stream Tests", () => { ...[0x02, 0x00, 0x00, 0x00].reverse(), ...[0x03, 0x00, 0x00, 0x00].reverse() ]); - const values = stream.readValue(FIT.BaseType.UINT32, 12, { endianness: Stream.BIG_ENDIAN }); + const values = stream.readValue(FIT.BaseType.UINT32, 12, { littleEndian: false, isArray: true }); expect(values.length).toBe(3); expect(Array.from(new Uint32Array(values))).toEqual([1, 2, 3]); }); test("Read UInt64", () => { const stream = Stream.fromByteArray([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08].reverse()); - const value = stream.readUInt64({ endianness: Stream.BIG_ENDIAN }); + const value = stream.readUInt64({ littleEndian: false }); expect(value).toBe(0x0807060504030201n); }); test("Read Int64", () => { const stream = Stream.fromByteArray([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF].reverse()); - const value = stream.readInt64({ endianness: Stream.BIG_ENDIAN }); + const value = stream.readInt64({ littleEndian: false }); expect(value).toBe(-1n); }); @@ -316,32 +316,32 @@ describe("Stream Tests", () => { ...[0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00].reverse(), ...[0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00].reverse() ]); - const values = stream.readValue(FIT.BaseType.UINT64, 24, { endianness: Stream.BIG_ENDIAN }); + const values = stream.readValue(FIT.BaseType.UINT64, 24, { littleEndian: false, isArray: true }); expect(values.length).toBe(3); expect(Array.from(new BigUint64Array(values))).toEqual([1n, 2n, 3n]); }); test("Read Float32 Positive", () => { const stream = Stream.fromByteArray([0x00, 0x00, 0x80, 0x3F].reverse()); - const value = stream.readFloat32({ endianness: Stream.BIG_ENDIAN }); + const value = stream.readFloat32({ littleEndian: false }); expect(value).toBe(1); }); test("Read Float32 Negative Value", () => { const stream = Stream.fromByteArray([0x00, 0x00, 0x80, 0xBF].reverse()); - const value = stream.readFloat32({ endianness: Stream.BIG_ENDIAN }); + const value = stream.readFloat32({ littleEndian: false }); expect(value).toBe(-1); }); test("Read Max Float32 Positive Value", () => { const stream = Stream.fromByteArray([0xFF, 0xFF, 0x7F, 0x7F].reverse()); - const value = stream.readFloat32({ endianness: Stream.BIG_ENDIAN }); + const value = stream.readFloat32({ littleEndian: false }); expect(value).toBeCloseTo(3.4028234663852886e+38); }); test("Read Max Float32 Negative Value", () => { const stream = Stream.fromByteArray([0xFF, 0xFF, 0x7F, 0xFF].reverse()); - const value = stream.readFloat32({ endianness: Stream.BIG_ENDIAN }); + const value = stream.readFloat32({ littleEndian: false }); expect(value).toBeCloseTo(-3.4028234663852886e+38); }); @@ -351,20 +351,20 @@ describe("Stream Tests", () => { ...[0x00, 0x00, 0x00, 0x40].reverse(), ...[0x00, 0x00, 0x40, 0x40].reverse() ]); - const values = stream.readValue(FIT.BaseType.FLOAT32, 12, { endianness: Stream.BIG_ENDIAN }); + const values = stream.readValue(FIT.BaseType.FLOAT32, 12, { littleEndian: false, isArray: true }); expect(values.length).toBe(3); expect(Array.from(new Float32Array(values))).toEqual([1.0, 2.0, 3.0]); }); test("Read Float64 Positive", () => { const stream = Stream.fromByteArray([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F].reverse()); - const value = stream.readFloat64({ endianness: Stream.BIG_ENDIAN }); + const value = stream.readFloat64({ littleEndian: false }); expect(value).toBe(1); }); test("Read Float64 Negative", () => { const stream = Stream.fromByteArray([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xBF].reverse()); - const value = stream.readFloat64({ endianness: Stream.BIG_ENDIAN }); + const value = stream.readFloat64({ littleEndian: false }); expect(-1).toBe(value); }); @@ -374,7 +374,7 @@ describe("Stream Tests", () => { ...[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40].reverse(), ...[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x40].reverse() ]); - const values = stream.readValue(FIT.BaseType.FLOAT64, 24, { endianness: Stream.BIG_ENDIAN }); + const values = stream.readValue(FIT.BaseType.FLOAT64, 24, { littleEndian: false, isArray: true }); expect(values.length).toBe(3); expect([1.0, 2.0, 3.0]).toEqual(Array.from(new Float64Array(values))); }); @@ -412,15 +412,33 @@ describe("Stream Tests", () => { test("Read String Array w/o Null Terminator", () => { const stream = Stream.fromByteArray([0x2E, 0x46, 0x49, 0x54, 0x00, 0x2E, 0x46, 0x49, 0x54]); const values = stream.readString(9); - expect(values.length).toBe(2); - values.forEach(value => expect(value).toBe(".FIT")); + expect(values).toBe(".FIT"); }); test("Read String Array w/ Null Terminator", () => { const stream = Stream.fromByteArray([0x2E, 0x46, 0x49, 0x54, 0x00, 0x2E, 0x46, 0x49, 0x54, 0x00]); const values = stream.readString(10); - expect(values.length).toBe(2); - values.forEach(value => expect(value).toBe(".FIT")); + expect(values).toBe(".FIT"); + }); + + describe("LegacyArrayMode String Tests", () => { + test("Read String Array w/o Null Terminator and Preserves Array", () => { + const stream = Stream.fromByteArray([0x2E, 0x46, 0x49, 0x54, 0x00, 0x2E, 0x46, 0x49, 0x54]); + stream.legacyArrayMode = true; + + const values = stream.readString(9); + expect(values.length).toBe(2); + values.forEach(value => expect(value).toBe(".FIT")); + }); + + test("Read String Array w/ Null Terminator and Preserves Array", () => { + const stream = Stream.fromByteArray([0x2E, 0x46, 0x49, 0x54, 0x00, 0x2E, 0x46, 0x49, 0x54, 0x00]); + stream.legacyArrayMode = true; + + const values = stream.readString(10); + expect(values.length).toBe(2); + values.forEach(value => expect(value).toBe(".FIT")); + }); }); test("fitFile_CA76065_WithBadUtf8CharactersTest", () => { @@ -439,7 +457,10 @@ describe("Stream Tests", () => { 0x61, 0x63, 0x65, 0x2E, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0xD7, 0x7C, 0x37, 0x35, 0x25, 0x20, 0x65, 0x66]); + + stream.legacyArrayMode = true; const values = stream.readString(200); + expect(values.length).toBe(54); expect(values[0]).toBe("75% effort."); expect(values[6]).toBe("un"); // Not "����un" diff --git a/test/types.test-d.ts b/test/types.test-d.ts new file mode 100644 index 0000000..a15647b --- /dev/null +++ b/test/types.test-d.ts @@ -0,0 +1,338 @@ +///////////////////////////////////////////////////////////////////////////////////////////// +// Copyright 2026 Garmin International, Inc. +// Licensed under the Flexible and Interoperable Data Transfer (FIT) Protocol License; you +// may not use this file except in compliance with the Flexible and Interoperable Data +// Transfer (FIT) Protocol License. +///////////////////////////////////////////////////////////////////////////////////////////// + + +import { expectTypeOf, test, expect, describe } from "vitest"; + +import * as FIT from "../src/index"; + +describe("Decoder Type Tests", () => { + test("Decoder Constructor", () => { + expectTypeOf(FIT.Decoder).constructorParameters.toEqualTypeOf<[stream: FIT.Stream]>(); + + // @ts-expect-error + expectTypeOf(FIT.Decoder).constructorParameters.toEqualTypeOf(); + }); + + test("Decoder isFIT instance", () => { + const decoder = new FIT.Decoder(FIT.Stream.fromByteArray(new Uint8Array([0x00, 0x00, 0x00, 0x00]))); + + const isFitInstance = decoder.isFIT(); + expectTypeOf(isFitInstance).toEqualTypeOf(); + expect(isFitInstance).toBe(false); + }); + + test("Decoder isFIT static", () => { + expectTypeOf(FIT.Decoder.isFIT).parameters.toEqualTypeOf<[stream: FIT.Stream]>(); + + const isFitStatic = FIT.Decoder.isFIT(FIT.Stream.fromByteArray(new Uint8Array([0x00, 0x00, 0x00, 0x00]))); + expectTypeOf(isFitStatic).toEqualTypeOf(); + expect(isFitStatic).toBe(false); + }); + + test("Decoder checkIntegrity", () => { + const decoder = new FIT.Decoder(FIT.Stream.fromByteArray(new Uint8Array([0x00, 0x00, 0x00, 0x00]))); + expectTypeOf(decoder.checkIntegrity).returns.toEqualTypeOf(); + }); + + test("Decoder read", () => { + const decoder = new FIT.Decoder(FIT.Stream.fromByteArray(new Uint8Array([0x00, 0x00, 0x00, 0x00]))); + const result = decoder.read(); + + expectTypeOf(result).toEqualTypeOf<{ messages: FIT.FitMessages; profileVersion: FIT.ProfileVersion; errors: Error[] }>(); + expectTypeOf(result.profileVersion).toEqualTypeOf(); + + const version: FIT.ProfileVersion = { major: 1, minor: 0 }; + expectTypeOf(version.major).toEqualTypeOf(); + expectTypeOf(version.minor).toEqualTypeOf(); + }); + + test("DecoderOptions", () => { + const options: FIT.DecoderOptions = { + mesgListener: (mesgNum, mesg) => { + expectTypeOf(mesgNum).toEqualTypeOf(); + expectTypeOf(mesg).toEqualTypeOf(); + }, + mesgDefinitionListener: (mesgDefinition) => { + expectTypeOf(mesgDefinition).toEqualTypeOf(); + }, + fieldDescriptionListener: (key, developerDataIdMesg, fieldDescriptionMesg) => { + expectTypeOf(key).toEqualTypeOf(); + expectTypeOf(developerDataIdMesg).toEqualTypeOf(); + expectTypeOf(fieldDescriptionMesg).toEqualTypeOf(); + }, + expandSubFields: true, + expandComponents: true, + applyScaleAndOffset: true, + convertTypesToStrings: true, + convertDateTimesToDates: true, + includeUnknownData: false, + mergeHeartRates: false, + decodeMemoGlobs: false, + skipHeader: false, + dataOnly: false, + }; + expectTypeOf(options).toEqualTypeOf(); + }); + + test("MesgDefinition and FieldDefinition", () => { + const fieldDef: FIT.FieldDefinition = { + fieldDefinitionNumber: 0, + size: 1, + baseType: 0, + invalidValue: 0xff, + baseTypeSize: 1, + }; + expectTypeOf(fieldDef.fieldDefinitionNumber).toEqualTypeOf(); + expectTypeOf(fieldDef.size).toEqualTypeOf(); + expectTypeOf(fieldDef.baseType).toEqualTypeOf(); + expectTypeOf(fieldDef.invalidValue).toEqualTypeOf(); + expectTypeOf(fieldDef.baseTypeSize).toEqualTypeOf(); + + const devFieldDef: FIT.DeveloperFieldDefinition = { + fieldDefinitionNumber: 0, + size: 1, + developerDataIndex: 0, + }; + expectTypeOf(devFieldDef.developerDataIndex).toEqualTypeOf(); + }); +}); + +describe("Stream Type Tests", () => { + test("Stream constructor", () => { + expectTypeOf(FIT.Stream).constructorParameters.toEqualTypeOf<[buffer: ArrayBuffer]>(); + }); + + test("Stream static factory methods", () => { + expectTypeOf(FIT.Stream.fromByteArray).parameters.toEqualTypeOf<[data: number[] | Uint8Array]>(); + expectTypeOf(FIT.Stream.fromByteArray).returns.toEqualTypeOf(); + + expectTypeOf(FIT.Stream.fromBuffer).parameters.toEqualTypeOf<[buffer: Uint8Array]>(); + expectTypeOf(FIT.Stream.fromBuffer).returns.toEqualTypeOf(); + + expectTypeOf(FIT.Stream.fromArrayBuffer).parameters.toEqualTypeOf<[buffer: ArrayBuffer]>(); + expectTypeOf(FIT.Stream.fromArrayBuffer).returns.toEqualTypeOf(); + }); + + test("Stream instance properties", () => { + const stream = FIT.Stream.fromByteArray(new Uint8Array([0x00])); + + expectTypeOf(stream.length).toEqualTypeOf(); + expectTypeOf(stream.bytesRead).toEqualTypeOf(); + expectTypeOf(stream.position).toEqualTypeOf(); + expectTypeOf(stream.crcCalculator).toEqualTypeOf(); + }); + + test("Stream navigation methods", () => { + const stream = FIT.Stream.fromByteArray(new Uint8Array([0x00])); + + expectTypeOf(stream.reset).returns.toEqualTypeOf(); + expectTypeOf(stream.seek).parameters.toEqualTypeOf<[position: number]>(); + expectTypeOf(stream.seek).returns.toEqualTypeOf(); + expectTypeOf(stream.slice).parameters.toEqualTypeOf<[begin: number, end: number]>(); + expectTypeOf(stream.slice).returns.toEqualTypeOf(); + expectTypeOf(stream.peekByte).returns.toEqualTypeOf(); + expectTypeOf(stream.readByte).returns.toEqualTypeOf(); + expectTypeOf(stream.readBytes).parameters.toEqualTypeOf<[size: number]>(); + expectTypeOf(stream.readBytes).returns.toEqualTypeOf(); + }); + + test("Stream readValue", () => { + const stream = FIT.Stream.fromByteArray(new Uint8Array([0x00])); + + expectTypeOf(stream.readValue).parameters.toEqualTypeOf<[baseType: number, size: number, opts?: FIT.ReadValueOptions]>(); + expectTypeOf(stream.readValue).returns.toEqualTypeOf(); + }); + + test("ReadValueOptions", () => { + const opts: FIT.ReadValueOptions = { + littleEndian: true, + convertInvalidToNull: true, + }; + expectTypeOf(opts.littleEndian).toEqualTypeOf(); + expectTypeOf(opts.convertInvalidToNull).toEqualTypeOf(); + }); + + test("Stream typed read methods", () => { + const stream = FIT.Stream.fromByteArray(new Uint8Array([0x00])); + + expectTypeOf(stream.readUInt8).returns.toEqualTypeOf(); + expectTypeOf(stream.readInt8).returns.toEqualTypeOf(); + expectTypeOf(stream.readUInt16).returns.toEqualTypeOf(); + expectTypeOf(stream.readInt16).returns.toEqualTypeOf(); + expectTypeOf(stream.readUInt32).returns.toEqualTypeOf(); + expectTypeOf(stream.readInt32).returns.toEqualTypeOf(); + expectTypeOf(stream.readUInt64).returns.toEqualTypeOf(); + expectTypeOf(stream.readInt64).returns.toEqualTypeOf(); + expectTypeOf(stream.readFloat32).returns.toEqualTypeOf(); + expectTypeOf(stream.readFloat64).returns.toEqualTypeOf(); + expectTypeOf(stream.readString).parameters.toEqualTypeOf<[strlen: number]>(); + expectTypeOf(stream.readString).returns.toEqualTypeOf(); + }); +}); + +describe("Encoder Type Tests", () => { + test("Encoder constructor", () => { + expectTypeOf(FIT.Encoder).constructorParameters.toEqualTypeOf<[options?: FIT.EncoderOptions]>(); + }); + + test("Encoder close", () => { + const encoder = new FIT.Encoder(); + expectTypeOf(encoder.close).returns.toEqualTypeOf(); + }); + + test("Encoder writeMesg", () => { + const encoder = new FIT.Encoder(); + expectTypeOf(encoder.writeMesg).parameters.toEqualTypeOf<[mesg: FIT.Encodable]>(); + expectTypeOf(encoder.writeMesg).returns.toEqualTypeOf(); + }); + + test("Encoder onMesg", () => { + const encoder = new FIT.Encoder(); + expectTypeOf(encoder.onMesg).parameters.toEqualTypeOf<[mesgNum: number, mesg: FIT.Mesg]>(); + expectTypeOf(encoder.onMesg).returns.toEqualTypeOf(); + }); + + test("Encoder addDeveloperField", () => { + const encoder = new FIT.Encoder(); + expectTypeOf(encoder.addDeveloperField).parameters.toEqualTypeOf<[key: number, developerDataIdMesg: FIT.DeveloperDataIdMesg, fieldDescriptionMesg: FIT.FieldDescriptionMesg]>(); + expectTypeOf(encoder.addDeveloperField).returns.toEqualTypeOf(); + }); + + test("EncoderOptions and FieldDescription", () => { + const fieldDescription: FIT.FieldDescription = { + developerDataIdMesg: { developerDataIndex: 0 }, + fieldDescriptionMesg: { developerDataIndex: 0, fieldDefinitionNumber: 0 }, + }; + expectTypeOf(fieldDescription.developerDataIdMesg).toEqualTypeOf(); + expectTypeOf(fieldDescription.fieldDescriptionMesg).toEqualTypeOf(); + + const options: FIT.EncoderOptions = { + fieldDescriptions: { 0: fieldDescription }, + }; + expectTypeOf(options.fieldDescriptions).toEqualTypeOf | undefined>(); + }); +}); + +describe("Mesg Type Tests", () => { + test("FileIdMesg fields", () => { + const fileIdMesg: FIT.FileIdMesg = { + type: "activity", + manufacturer: "garmin", + garminProduct: 123, + productName: "Test Product", + timeCreated: new Date(), + developerFields: { + customField1: "Custom Value", + customField2: 42, + }, + }; + expectTypeOf(fileIdMesg).toExtend; + expectTypeOf(fileIdMesg).toEqualTypeOf; + + expectTypeOf(fileIdMesg.type).toEqualTypeOf(); + expectTypeOf(fileIdMesg.manufacturer).toEqualTypeOf(); + expectTypeOf(fileIdMesg.garminProduct).toEqualTypeOf(); + expectTypeOf(fileIdMesg.productName).toEqualTypeOf(); + expectTypeOf(fileIdMesg.timeCreated).toEqualTypeOf(); + expectTypeOf(fileIdMesg.developerFields).toEqualTypeOf(); + }); + + test("Mesg base interface", () => { + const mesg: FIT.Mesg = {}; + expectTypeOf(mesg.developerFields).toEqualTypeOf(); + }); + + test("DeveloperFields type", () => { + const devFields: FIT.DeveloperFields = { + myField: 123, + myOtherField: "value", + }; + expectTypeOf(devFields).toEqualTypeOf(); + }); +}); + +describe("CrcCalculator Type Tests", () => { + test("CrcCalculator constructor and properties", () => { + const crc = new FIT.CrcCalculator(); + expectTypeOf(crc.crc).toEqualTypeOf(); + }); + + test("CrcCalculator instance method", () => { + const crc = new FIT.CrcCalculator(); + expectTypeOf(crc.addBytes).parameters.toEqualTypeOf<[buf: Uint8Array, start: number, end: number]>(); + expectTypeOf(crc.addBytes).returns.toEqualTypeOf(); + }); + + test("CrcCalculator static method", () => { + expectTypeOf(FIT.CrcCalculator.calculateCRC).parameters.toEqualTypeOf<[buf: Uint8Array, start: number, end: number]>(); + expectTypeOf(FIT.CrcCalculator.calculateCRC).returns.toEqualTypeOf(); + }); +}); + +describe("Profile Type Tests", () => { + test("Profile version", () => { + expectTypeOf(FIT.Profile.version.major).toEqualTypeOf(); + expectTypeOf(FIT.Profile.version.minor).toEqualTypeOf(); + expectTypeOf(FIT.Profile.version.patch).toEqualTypeOf(); + expectTypeOf(FIT.Profile.version.type).toEqualTypeOf(); + }); + + test("Profile MesgNum", () => { + expectTypeOf(FIT.Profile.MesgNum).toEqualTypeOf>(); + expectTypeOf(FIT.Profile.MesgNum.FILE_ID).toEqualTypeOf(); + }); + + test("Profile messages", () => { + expectTypeOf(FIT.Profile.messages).toEqualTypeOf>(); + }); + + test("ProfileMesg and ProfileField", () => { + const mesg: FIT.ProfileMesg = FIT.Profile.messages[FIT.Profile.MesgNum.FILE_ID]; + expectTypeOf(mesg.num).toEqualTypeOf(); + expectTypeOf(mesg.name).toEqualTypeOf(); + expectTypeOf(mesg.messagesKey).toEqualTypeOf(); + expectTypeOf(mesg.fields).toEqualTypeOf>(); + }); +}); + +describe("Utils Type Tests", () => { + test("Utils constants", () => { + expectTypeOf(FIT.Utils.FIT_EPOCH_MS).toEqualTypeOf(); + expectTypeOf(FIT.Utils.FitBaseType).toEqualTypeOf<{ + readonly ENUM: number; + readonly SINT8: number; + readonly UINT8: number; + readonly SINT16: number; + readonly UINT16: number; + readonly SINT32: number; + readonly UINT32: number; + readonly STRING: number; + readonly FLOAT32: number; + readonly FLOAT64: number; + readonly UINT8Z: number; + readonly UINT16Z: number; + readonly UINT32Z: number; + readonly BYTE: number; + readonly SINT64: number; + readonly UINT64: number; + readonly UINT64Z: number; + }>(); + }); + + test("Utils date conversion methods", () => { + expectTypeOf(FIT.Utils.convertDateTimeToDate).parameters.toEqualTypeOf<[datetime: number]>(); + expectTypeOf(FIT.Utils.convertDateTimeToDate).returns.toEqualTypeOf(); + expectTypeOf(FIT.Utils.convertDateToDateTime).parameters.toEqualTypeOf<[date: Date]>(); + expectTypeOf(FIT.Utils.convertDateToDateTime).returns.toEqualTypeOf(); + }); + + test("Utils base type lookup maps", () => { + expectTypeOf(FIT.Utils.BaseTypeToFieldType).toEqualTypeOf>(); + expectTypeOf(FIT.Utils.FieldTypeToBaseType).toEqualTypeOf>(); + }); +}); diff --git a/test/utils/fit-builder.js b/test/utils/fit-builder.js new file mode 100644 index 0000000..ec19a43 --- /dev/null +++ b/test/utils/fit-builder.js @@ -0,0 +1,46 @@ +import CRC from "../../src/crc-calculator.js"; +import FIT from "../../src/fit.js"; + +export const uint16LE = (value) => { + const buf = new ArrayBuffer(2); + new DataView(buf).setUint16(0, value, true); + return [...new Uint8Array(buf)]; +} + +export const uint32LE = (value) => { + const buf = new ArrayBuffer(4); + new DataView(buf).setUint32(0, value, true); + return [...new Uint8Array(buf)]; +} + +export const uint64LE = (value) => { + const buf = new ArrayBuffer(8); + new DataView(buf).setBigUint64(0, BigInt(value), true); + return [...new Uint8Array(buf)]; +} + +export const buildFit = (mesgNum, fieldDefs, records) => { + const defn = [0x40, 0x00, 0x00, mesgNum & 0xFF, (mesgNum >> 8) & 0xFF, fieldDefs.length]; + for (const [fieldNum, sizeBytes, baseType] of fieldDefs) { + defn.push(fieldNum, sizeBytes, baseType | FIT.BaseTypeDefinitions[baseType].baseTypeEndianFlag); + } + + const data = []; + for (const rec of records) { + data.push(0x00, ...rec); + } + + const dataSection = [...defn, ...data]; + + const header = [0x0E, 0x20, 0xE8, 0x03]; + const dataSize = dataSection.length; + header.push(dataSize & 0xFF, (dataSize >> 8) & 0xFF, (dataSize >> 16) & 0xFF, (dataSize >> 24) & 0xFF); + header.push(0x2E, 0x46, 0x49, 0x54); + const headerCrc = CRC.calculateCRC(header, 0, 12); + header.push(headerCrc & 0xFF, (headerCrc >> 8) & 0xFF); + + const full = [...header, ...dataSection]; + const fileCrc = CRC.calculateCRC(full, 0, full.length); + full.push(fileCrc & 0xFF, (fileCrc >> 8) & 0xFF); + return full; +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..de88733 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "bundler", + "allowJs": true, + "strict": true, + "noEmit": true, + "types": [ + "node" + ] + }, + "include": [ + "src/types/**/*.d.ts", + "src/*.js", + "test/**/*.test.ts", + "test/**/*.test-d.ts" + ], +} \ No newline at end of file