tirecheck-device-sdk 0.2.45 → 0.2.46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -117,12 +117,16 @@ function decimalToHex(decimal, padStart = 2) {
117
117
  const hex = decimal.toString(16).toUpperCase();
118
118
  return hex.padStart(padStart, "0");
119
119
  }
120
+ function removeEndLine(string) {
121
+ return string.replace("\r\n", "");
122
+ }
120
123
  const toolsSvc = {
121
124
  delay,
122
125
  setIntervalImmediate,
123
126
  waitUntil,
124
127
  withTimeout,
125
- canCommunicateWith
128
+ canCommunicateWith,
129
+ removeEndLine
126
130
  };
127
131
  function normalizeError(error) {
128
132
  if (!error) return new Error("Operation timed out");
@@ -192,7 +196,7 @@ const bridgeTools = {
192
196
  decodeData(data, displayUnits) {
193
197
  const _data = ___default.clone(data);
194
198
  if (displayUnits === "ascii") {
195
- return _data.filter((d) => d).map((x) => String.fromCharCode(x)).join("");
199
+ return String.fromCharCode(..._data.filter((d) => d));
196
200
  }
197
201
  const _reversedData = _data.reverse();
198
202
  if (displayUnits === "decimal") {
@@ -2846,7 +2850,7 @@ const flexiGaugeTpmsService$1 = {
2846
2850
  icon: "icon-flexigauge",
2847
2851
  processMessage(deviceId, message) {
2848
2852
  const numberArray = Array.from(new Uint8Array(message));
2849
- const stringFromBytes = String.fromCharCode.apply(null, numberArray).replace("\r\n", "");
2853
+ const stringFromBytes = removeEndLine(String.fromCharCode(...numberArray));
2850
2854
  for (const capability of capabilities$2) {
2851
2855
  if (capability.regex.test(stringFromBytes)) {
2852
2856
  return capability.processFn(deviceId, stringFromBytes);
@@ -2919,8 +2923,8 @@ const promiseQueue = {
2919
2923
  chunks.push(payload.slice(i, i + mtu));
2920
2924
  }
2921
2925
  for (const chunk of chunks) {
2922
- const convertedChunk = new Uint8Array(chunk).buffer;
2923
- bluetooth.write(deviceId, communication.serviceId, communication.characteristicId, convertedChunk);
2926
+ const convertedChunk = new Uint8Array(chunk);
2927
+ bluetooth.write(deviceId, communication.serviceId, communication.characteristicId, convertedChunk.buffer);
2924
2928
  }
2925
2929
  });
2926
2930
  return withTimeout(promise, 5e3, `Command timed out ${deviceResponseIdentifier[deviceId]}, ${deviceId}`);
@@ -3004,8 +3008,28 @@ const flexiGaugeTpmsCommands = {
3004
3008
  vdaRequestCommandsAccess,
3005
3009
  vdaRequestWriteAccess,
3006
3010
  processMessage,
3007
- getFirmwareVersion: getFirmwareVersion$1
3011
+ getFirmwareVersion: getFirmwareVersion$2,
3012
+ getConfig,
3013
+ setConfig,
3014
+ getTpmsConfig,
3015
+ setTpmsConfig
3008
3016
  };
3017
+ async function getConfig(deviceId) {
3018
+ const msg = "*CONFIG?";
3019
+ return sendCommand(deviceId, msg);
3020
+ }
3021
+ async function setConfig(deviceId, config) {
3022
+ const msg = `*CONFIG ${config}`;
3023
+ return sendCommand(deviceId, msg);
3024
+ }
3025
+ async function getTpmsConfig(deviceId) {
3026
+ const msg = "*TPMS CONFIG?";
3027
+ return sendCommand(deviceId, msg);
3028
+ }
3029
+ async function setTpmsConfig(deviceId, config) {
3030
+ const msg = `*TPMS CONFIG ${config}`;
3031
+ return sendCommand(deviceId, msg);
3032
+ }
3009
3033
  async function startTpmsScan(deviceId) {
3010
3034
  if (!canCommunicateWith(deviceId)) throw new Error("Flexi Gauge not connected");
3011
3035
  const scanMsg = stringToArrayBuffer("*TPMS ON TIME=10\r\n");
@@ -3025,9 +3049,9 @@ async function getBattery(deviceId) {
3025
3049
  const battery = Array.from(new Uint8Array(batteryValue))[0];
3026
3050
  return battery;
3027
3051
  }
3028
- async function getFirmwareVersion$1(deviceId) {
3052
+ async function getFirmwareVersion$2(deviceId) {
3029
3053
  const msg = "*FWVER?";
3030
- return sendCommand(deviceId, msg, "FWV");
3054
+ return sendCommand(deviceId, msg);
3031
3055
  }
3032
3056
  async function vdaRequestSensor(deviceId) {
3033
3057
  const scanMsg = "*VDA_LF_SEND SID=A0 LID=1A";
@@ -3067,9 +3091,10 @@ async function vdaSendConfiguration(deviceId, configuration) {
3067
3091
  const scanMsg = `*VDA_LF_SEND SID=A0 LID=EA PAR=17 DATA=${configuration}${crc}`;
3068
3092
  return sendCommand(deviceId, scanMsg);
3069
3093
  }
3070
- async function sendCommand(deviceId, command, identifier = "VDA") {
3094
+ async function sendCommand(deviceId, command) {
3071
3095
  const message = `${command}\r
3072
3096
  `;
3097
+ const identifier = getIdentifier$1(message);
3073
3098
  const decimalArray = stringToDecimalArray(message);
3074
3099
  let result;
3075
3100
  try {
@@ -3078,7 +3103,7 @@ async function sendCommand(deviceId, command, identifier = "VDA") {
3078
3103
  flexiGaugeTpms.disconnect(deviceId, "lostConnection");
3079
3104
  throw error;
3080
3105
  }
3081
- return result.map((num) => String.fromCharCode(num)).join("");
3106
+ return String.fromCharCode(...result);
3082
3107
  }
3083
3108
  function processMessage(deviceId, payload) {
3084
3109
  promiseQueue.processMessage(deviceId, payload, getIdentifier$1, isComplete$1, isError$1);
@@ -3091,13 +3116,163 @@ function isError$1(message) {
3091
3116
  return false;
3092
3117
  }
3093
3118
  function getIdentifier$1(message) {
3094
- const messageString = message.map((num) => String.fromCharCode(num)).join("");
3095
- return messageString.slice(0, 3);
3119
+ const messageString = typeof message === "string" ? message : String.fromCharCode(...message);
3120
+ const [, commands] = removeEndLine(messageString).match(/\*?([^=|^?]+)/) || [];
3121
+ const [identifier, subIdentifier] = commands.split(" ") || [];
3122
+ return identifier === "TPMS" && subIdentifier ? `${identifier} ${subIdentifier}` : identifier;
3096
3123
  }
3097
3124
  function getReversedSensorId(sensorId) {
3098
3125
  return sensorId.match(/.{1,2}/g).reverse().join("");
3099
3126
  }
3100
3127
 
3128
+ const fgConfigPressureUnits = ["bar", "psi", "kpa"];
3129
+ const fgConfigTdUnits = ["mm", "thirtySecondOfInch"];
3130
+ const fgConfigNeedleLengths = [15, 30, 40, 100, 130];
3131
+ const fgConfigRegions = ["eu", "us"];
3132
+ const fgConfigPressureDisplay = ["measured", "compensated"];
3133
+ const fgConfigTemperatureUnits = ["fahrenheit", "celsius"];
3134
+ const configToJsonMapping = {
3135
+ ML: (v) => ({ menuLock: v === "1" }),
3136
+ PU: (v) => ({ pressureUnit: fgConfigPressureUnits[Number(v)] }),
3137
+ TU: (v) => ({ tdUnit: fgConfigTdUnits[Number(v)] }),
3138
+ NL: (v) => ({ needleLength: fgConfigNeedleLengths[Number(v)] }),
3139
+ BE: (v) => ({ beep: v === "1" }),
3140
+ RS: (v) => ({ region: fgConfigRegions[Number(v)] }),
3141
+ PS: (v) => ({ powerSave: Number(v) }),
3142
+ PD: (v) => ({ pressureDisplay: fgConfigPressureDisplay[Number(v)] }),
3143
+ FW: (v) => ({ fwUpdate: v === "1" }),
3144
+ TT: (v) => ({ temperatureUnit: fgConfigTemperatureUnits[Number(v)] })
3145
+ };
3146
+ const configFromJsonMapping = {
3147
+ menuLock: (v) => `ML=${v ? "1" : "0"}`,
3148
+ pressureUnit: (v) => `PU=${getIndex(fgConfigPressureUnits, v)}`,
3149
+ tdUnit: (v) => `TU=${getIndex(fgConfigTdUnits, v)}`,
3150
+ needleLength: (v) => `NL=${getIndex(fgConfigNeedleLengths, v)}`,
3151
+ beep: (v) => `BE=${v ? "1" : "0"}`,
3152
+ region: (v) => `RS=${getIndex(fgConfigRegions, v)}`,
3153
+ powerSave: (v) => `PS=${v.toString()}`,
3154
+ pressureDisplay: (v) => `PD=${getIndex(fgConfigPressureDisplay, v)}`,
3155
+ fwUpdate: (v) => `FW=${v ? "1" : "0"}`,
3156
+ temperatureUnit: (v) => `TT=${getIndex(fgConfigTemperatureUnits, v)}`
3157
+ };
3158
+ const fgTpmsConfigProtocols = [
3159
+ "pwmTirecheck",
3160
+ "vdaTirecheck",
3161
+ "pwmWabco",
3162
+ "vdaLdl",
3163
+ "vdaContinental",
3164
+ "vdaSchrader"
3165
+ ];
3166
+ const flexiGaugeTpmsServiceMapping = {
3167
+ getDefaultConfigJson() {
3168
+ return {
3169
+ menuLock: false,
3170
+ pressureUnit: "bar",
3171
+ tdUnit: "mm",
3172
+ needleLength: 40,
3173
+ beep: true,
3174
+ region: "eu",
3175
+ powerSave: 15,
3176
+ pressureDisplay: "measured",
3177
+ fwUpdate: false,
3178
+ temperatureUnit: "fahrenheit"
3179
+ };
3180
+ },
3181
+ getDefaultTpmsConfigJson() {
3182
+ return ___default.cloneDeep({
3183
+ protocols: fgTpmsConfigProtocols.map((id) => ({ id, time: 5 }))
3184
+ });
3185
+ },
3186
+ /**
3187
+ * @param configString example: `CONFIG ML=0 PU=0 TU=0 NL=2 BE=1 RS=0 PS=15 PD=0 FW=0 TT=0\r\n`
3188
+ */
3189
+ getConfigJson(configString) {
3190
+ let config = this.getDefaultConfigJson();
3191
+ const parts = toolsSvc.removeEndLine(configString).split(" ").slice(1).map((v) => v.split("="));
3192
+ for (const [name, value] of parts) {
3193
+ const toJsonFn = configToJsonMapping[name];
3194
+ config = { ...config, ...toJsonFn(value) };
3195
+ }
3196
+ return config;
3197
+ },
3198
+ /**
3199
+ * @returns example: `ML=0 PU=0 TU=0 NL=2 BE=1 RS=0 PS=15 PD=0 FW=0 TT=0`
3200
+ */
3201
+ getConfig(configJson) {
3202
+ const configString = Object.entries(configJson).map(([key, value]) => {
3203
+ const fromJsonFn = configFromJsonMapping[key];
3204
+ return fromJsonFn(value);
3205
+ }).join(" ");
3206
+ return configString;
3207
+ },
3208
+ /**
3209
+ * @param configString example: `TPMS CONFIG ALG=0001020304050000 TIME=5050505050500000\r\n`
3210
+ *
3211
+ * - every protocol/time is 2 bytes
3212
+ * - we only support 6 protocols, rest is empty
3213
+ * - time is stored as n*100ms (`50` = 50x100ms = 5s)
3214
+ * - protocols can be duplicate when set in Fg Menu
3215
+ *
3216
+ * protocol codes:
3217
+ * - `00` - PWM Tirecheck - `pwmTirecheck`
3218
+ * - `01` - VDA Tirecheck - `vdaTirecheck`
3219
+ * - `02` - PWM WABCO - `pwmWabco`
3220
+ * - `03` - VDA LDL - `vdaLdl`
3221
+ * - `04` - VDA Continental - `vdaContinental`
3222
+ * - `05` - VDA Schrader - `vdaSchrader`
3223
+ */
3224
+ getTpmsConfigJson(configString) {
3225
+ const config = this.getDefaultTpmsConfigJson();
3226
+ const parts = toolsSvc.removeEndLine(configString).split(" ").slice(2).map((v) => {
3227
+ const [name, value] = v.split("=");
3228
+ if (name === "ALG") return ["protocols", getTpmsConfigProtocolList(value)];
3229
+ if (name === "TIME") return ["times", getTpmsConfigProtocolTimeList(value)];
3230
+ throw new Error(`Unknown Fg Tpms config part: ${name}`);
3231
+ });
3232
+ const pairs = ___default.fromPairs(parts);
3233
+ const zipped = ___default.zipObject(pairs.protocols, pairs.times);
3234
+ if (pairs.protocols.length) config.protocols = [];
3235
+ for (const [protocol, time] of Object.entries(zipped)) {
3236
+ config.protocols.push({ id: protocol, time: time / 10 });
3237
+ }
3238
+ if (config.protocols.length < 6) {
3239
+ const missingProtocols = fgTpmsConfigProtocols.filter((id) => !config.protocols.find((p) => p.id === id));
3240
+ for (const protocol of missingProtocols) {
3241
+ config.protocols.push({ id: protocol, time: 0 });
3242
+ }
3243
+ }
3244
+ return config;
3245
+ },
3246
+ /**
3247
+ * @returns example: `ALG=0001020304050000 TIME=5050505050500000`
3248
+ */
3249
+ getTpmsConfig(tpmsConfigJson) {
3250
+ const unzipped = tpmsConfigJson.protocols.reduce(
3251
+ (acc, { id, time }) => {
3252
+ acc.protocols.push(getIndex(fgTpmsConfigProtocols, id).padStart(2, "0"));
3253
+ if (time >= 10) throw new Error(`Fg Tpms config time value out of range: ${time}`);
3254
+ acc.times.push(
3255
+ Math.round(time * 10).toString().padStart(2, "0")
3256
+ );
3257
+ return acc;
3258
+ },
3259
+ { protocols: [], times: [] }
3260
+ );
3261
+ const protocols = unzipped.protocols.join("").padEnd(16, "0");
3262
+ const times = unzipped.times.join("").padEnd(16, "0");
3263
+ return `ALG=${protocols} TIME=${times}`;
3264
+ }
3265
+ };
3266
+ function getIndex(array, value) {
3267
+ return array.indexOf(value).toString();
3268
+ }
3269
+ function getTpmsConfigProtocolList(configString) {
3270
+ return configString.match(/(\d{2})/g)?.slice(0, 6).map((v) => fgTpmsConfigProtocols[Number.parseInt(v)]);
3271
+ }
3272
+ function getTpmsConfigProtocolTimeList(configString) {
3273
+ return configString.match(/(\d{2})/g)?.slice(0, 6).map((v) => Number.parseInt(v));
3274
+ }
3275
+
3101
3276
  let sensorReadings = [];
3102
3277
  const capabilities$1 = [
3103
3278
  {
@@ -3114,7 +3289,7 @@ const capabilities$1 = [
3114
3289
  {
3115
3290
  id: "tpms",
3116
3291
  processFn: processTpms,
3117
- regex: /^\*?TPMS.*/
3292
+ regex: /^\*?TPMS (ISON|DATA|ENDSCANNING).*/
3118
3293
  }
3119
3294
  ];
3120
3295
  const flexiGaugeTpmsService = {
@@ -3136,7 +3311,7 @@ const flexiGaugeTpmsService = {
3136
3311
  icon: "icon-flexigauge",
3137
3312
  processMessage(deviceId, message) {
3138
3313
  const numberArray = Array.from(new Uint8Array(message));
3139
- const stringFromBytes = String.fromCharCode.apply(null, numberArray).replace("\r\n", "");
3314
+ const stringFromBytes = removeEndLine(String.fromCharCode(...numberArray));
3140
3315
  for (const capability of capabilities$1) {
3141
3316
  if (capability.regex.test(stringFromBytes)) {
3142
3317
  return capability.processFn(deviceId, stringFromBytes);
@@ -3144,8 +3319,28 @@ const flexiGaugeTpmsService = {
3144
3319
  }
3145
3320
  flexiGaugeTpmsCommands.processMessage(deviceId, message);
3146
3321
  },
3322
+ async getConfig(deviceId) {
3323
+ const response = await flexiGaugeTpmsCommands.getConfig(deviceId);
3324
+ return flexiGaugeTpmsServiceMapping.getConfigJson(response);
3325
+ },
3326
+ async setConfig(deviceId, configJson) {
3327
+ const fw = await getFirmwareVersion$1(deviceId);
3328
+ if (fw < "1.4.4") throw new Error("Config change is not supported on this firmware version");
3329
+ const config = flexiGaugeTpmsServiceMapping.getConfig(configJson);
3330
+ await flexiGaugeTpmsCommands.setConfig(deviceId, config);
3331
+ },
3332
+ async getTpmsConfig(deviceId) {
3333
+ const response = await flexiGaugeTpmsCommands.getTpmsConfig(deviceId);
3334
+ return flexiGaugeTpmsServiceMapping.getTpmsConfigJson(response);
3335
+ },
3336
+ async setTpmsConfig(deviceId, configJson) {
3337
+ const fw = await getFirmwareVersion$1(deviceId);
3338
+ if (fw < "1.4.4") throw new Error("TPMS Config change is not supported on this firmware version");
3339
+ const config = flexiGaugeTpmsServiceMapping.getTpmsConfig(configJson);
3340
+ await flexiGaugeTpmsCommands.setTpmsConfig(deviceId, config);
3341
+ },
3147
3342
  setSensorDisplayId,
3148
- getFirmwareVersion
3343
+ getFirmwareVersion: getFirmwareVersion$1
3149
3344
  };
3150
3345
  function processTreadDepth(deviceId, value) {
3151
3346
  const treadDepth = value.match(/\d+/)?.[0];
@@ -3185,7 +3380,7 @@ function processTpms(deviceId, value) {
3185
3380
  }
3186
3381
  }
3187
3382
  async function setSensorDisplayId(deviceId, oldSensorId, newSensorId) {
3188
- const fw = await getFirmwareVersion(deviceId);
3383
+ const fw = await getFirmwareVersion$1(deviceId);
3189
3384
  if (fw < "1.4.4") throw new Error("Programming sensors is not supported on this firmware version");
3190
3385
  await findSensor(deviceId, oldSensorId);
3191
3386
  const seed = await getSeed(deviceId, oldSensorId);
@@ -3234,10 +3429,10 @@ async function vdaRepeat(fn) {
3234
3429
  }
3235
3430
  throw new Error("VDA Timeout");
3236
3431
  }
3237
- async function getFirmwareVersion(deviceId) {
3432
+ async function getFirmwareVersion$1(deviceId) {
3238
3433
  const response = await flexiGaugeTpmsCommands.getFirmwareVersion(deviceId);
3239
- const groupedValue = response.split(" ");
3240
- return groupedValue[1];
3434
+ const [, version] = removeEndLine(response).split(" ");
3435
+ return version;
3241
3436
  }
3242
3437
 
3243
3438
  const flexiGaugeTpms = {
@@ -3267,7 +3462,11 @@ const flexiGaugeTpms = {
3267
3462
  getFirmwareVersion: flexiGaugeTpmsService.getFirmwareVersion,
3268
3463
  startTpmsScan: flexiGaugeTpmsService.startTpmsScan,
3269
3464
  setSensorDisplayId: flexiGaugeTpmsService.setSensorDisplayId,
3270
- resetSensorDisplayId: (deviceId, sensorId) => flexiGaugeTpmsService.setSensorDisplayId(deviceId, sensorId, "00000000")
3465
+ resetSensorDisplayId: (deviceId, sensorId) => flexiGaugeTpmsService.setSensorDisplayId(deviceId, sensorId, "00000000"),
3466
+ getConfig: flexiGaugeTpmsService.getConfig,
3467
+ setConfig: flexiGaugeTpmsService.setConfig,
3468
+ getTpmsConfig: flexiGaugeTpmsService.getTpmsConfig,
3469
+ setTpmsConfig: flexiGaugeTpmsService.setTpmsConfig
3271
3470
  };
3272
3471
 
3273
3472
  const capabilities = [
@@ -3283,7 +3482,7 @@ const pressureStickService = {
3283
3482
  },
3284
3483
  processMessage(deviceId, message) {
3285
3484
  const numberArray = Array.from(new Uint8Array(message));
3286
- const stringFromBytes = String.fromCharCode.apply(null, numberArray).replace("\r\n", "");
3485
+ const stringFromBytes = removeEndLine(String.fromCharCode(...numberArray));
3287
3486
  for (const capability of capabilities) {
3288
3487
  if (capability.regex.test(stringFromBytes)) {
3289
3488
  return capability.processFn(deviceId, stringFromBytes);
@@ -3383,7 +3582,7 @@ const torqueWrenchCommands = {
3383
3582
  const formattedError = formatError(error);
3384
3583
  throw new Error(formattedError);
3385
3584
  }
3386
- return result.map((num) => String.fromCharCode(num)).join("");
3585
+ return String.fromCharCode(...result);
3387
3586
  },
3388
3587
  processMessage(deviceId, payload) {
3389
3588
  promiseQueue.processMessage(deviceId, payload, getIdentifier, isComplete, isError);
@@ -3399,7 +3598,7 @@ function isError(message) {
3399
3598
  }
3400
3599
  function getIdentifier(message) {
3401
3600
  if (message[0] === 123) return "200";
3402
- return message.slice(6, 9).map((num) => String.fromCharCode(num)).join("");
3601
+ return String.fromCharCode(...message.slice(6, 9));
3403
3602
  }
3404
3603
  function getChecksum(message) {
3405
3604
  let checksum = 0;
@@ -3411,7 +3610,7 @@ function getChecksum(message) {
3411
3610
  function formatError(error) {
3412
3611
  if (!error.cause) throw error;
3413
3612
  const message = error.cause.slice(6, 8);
3414
- const errorId = message.map((num) => String.fromCharCode(num)).join("");
3613
+ const errorId = String.fromCharCode(...message);
3415
3614
  return errors[errorId] || "Unknown error";
3416
3615
  }
3417
3616
 
@@ -3947,10 +4146,45 @@ const flexiGaugeTpmsSimulator = {
3947
4146
  resetSensorDisplayId(deviceId, sensorId) {
3948
4147
  return new Promise((resolve) => resolve());
3949
4148
  },
3950
- async getFirmwareVersion(deviceId) {
4149
+ async getConfig(deviceId) {
4150
+ const fg = getSimulatedFg(deviceId);
3951
4151
  await toolsSvc.delay(100);
3952
- return "9.9.9";
4152
+ const fwVersion = await getFirmwareVersion(deviceId);
4153
+ if (fwVersion >= "1.4.4") {
4154
+ return { ...fg.simulatorData.config };
4155
+ }
4156
+ throw new Error(`Getting Fg config is not supported on firmware version ${fwVersion}, min 1.4.4 required`);
3953
4157
  },
4158
+ async setConfig(deviceId, configJson) {
4159
+ const fg = getSimulatedFg(deviceId);
4160
+ await toolsSvc.delay(100);
4161
+ const fwVersion = await getFirmwareVersion(deviceId);
4162
+ if (fwVersion >= "1.4.4") {
4163
+ fg.simulatorData.config = { ...configJson };
4164
+ return;
4165
+ }
4166
+ throw new Error(`Setting Fg config is not supported on firmware version ${fwVersion}, min 1.4.4 required`);
4167
+ },
4168
+ async getTpmsConfig(deviceId) {
4169
+ const fg = getSimulatedFg(deviceId);
4170
+ await toolsSvc.delay(100);
4171
+ const fwVersion = await getFirmwareVersion(deviceId);
4172
+ if (fwVersion >= "1.4.4") {
4173
+ return ___default.cloneDeep(fg.simulatorData.tpmsConfig);
4174
+ }
4175
+ throw new Error(`Getting Fg TPMS config is not supported on firmware version ${fwVersion}, min 1.4.4 required`);
4176
+ },
4177
+ async setTpmsConfig(deviceId, tpmsConfig) {
4178
+ const fg = getSimulatedFg(deviceId);
4179
+ await toolsSvc.delay(100);
4180
+ const fwVersion = await getFirmwareVersion(deviceId);
4181
+ if (fwVersion >= "1.4.4") {
4182
+ fg.simulatorData.tpmsConfig = ___default.cloneDeep(tpmsConfig);
4183
+ return;
4184
+ }
4185
+ throw new Error(`Setting Fg TPMS config is not supported on firmware version ${fwVersion}, min 1.4.4 required`);
4186
+ },
4187
+ getFirmwareVersion,
3954
4188
  onButtonPress(callback) {
3955
4189
  },
3956
4190
  onTpms(callback) {
@@ -3958,6 +4192,11 @@ const flexiGaugeTpmsSimulator = {
3958
4192
  onTreadDepth(callback) {
3959
4193
  }
3960
4194
  };
4195
+ async function getFirmwareVersion(deviceId) {
4196
+ const fg = getSimulatedFg(deviceId);
4197
+ await toolsSvc.delay(100);
4198
+ return fg.simulatorData.fwVersion;
4199
+ }
3961
4200
  function getSimulatedFg(deviceId) {
3962
4201
  const simulatedDevice = store.simulatedDevices[deviceId];
3963
4202
  if (!simulatedDevice) throw new Error(`Simulated bridge was not found: ${deviceId}`);
@@ -3971,12 +4210,12 @@ function getSimulatedFgTemplate() {
3971
4210
  type: "flexiGaugeTpms",
3972
4211
  rssi: -50,
3973
4212
  simulatorData: {
4213
+ fwVersion: "1.4.4",
3974
4214
  battery: 45,
3975
4215
  events: {
3976
4216
  "fg:button": "L",
3977
4217
  "fg:tpms": {
3978
- // TODO: replace with realistic values
3979
- ID: "FFFF1212",
4218
+ ID: generateEAN(268431361, 268435455),
3980
4219
  Rssi: "-50",
3981
4220
  PC: 12,
3982
4221
  T: 15,
@@ -3985,10 +4224,22 @@ function getSimulatedFgTemplate() {
3985
4224
  S: "S"
3986
4225
  },
3987
4226
  "fg:treadDepth": 5.2
3988
- }
4227
+ },
4228
+ config: flexiGaugeTpmsServiceMapping.getDefaultConfigJson(),
4229
+ tpmsConfig: flexiGaugeTpmsServiceMapping.getDefaultTpmsConfigJson()
3989
4230
  }
3990
4231
  };
3991
4232
  }
4233
+ function generateEAN(min, max) {
4234
+ const serial = Math.round(min + Math.random() * (max - min)).toString(16);
4235
+ let p1 = 0;
4236
+ for (let i = 0; i < serial.length; i++) {
4237
+ p1 += Number.parseInt(serial[i], 16) * (i % 2 === 1 ? 1 : 3);
4238
+ }
4239
+ const p2 = Math.ceil(p1 / 16) * 16;
4240
+ const check = p2 - p1;
4241
+ return `${serial}${check.toString(16)}`.toUpperCase();
4242
+ }
3992
4243
 
3993
4244
  const deviceServicesAndSimulators = [
3994
4245
  [bridge, bridgeSimulator],
package/dist/index.d.cts CHANGED
@@ -680,6 +680,34 @@ interface FgSensorReading {
680
680
  T: number;
681
681
  S?: string;
682
682
  }
683
+ type FgConfigPressureUnit = 'bar' | 'psi' | 'kpa';
684
+ type FgConfigTdUnit = 'mm' | 'thirtySecondOfInch';
685
+ type FgConfigNeedleLength = 15 | 30 | 40 | 100 | 130;
686
+ type FgConfigRegion = 'eu' | 'us';
687
+ type FgConfigPressureDisplay = 'measured' | 'compensated';
688
+ type FgConfigTemperatureUnit = 'fahrenheit' | 'celsius';
689
+ interface FgConfig {
690
+ menuLock?: boolean;
691
+ pressureUnit?: FgConfigPressureUnit;
692
+ tdUnit?: FgConfigTdUnit;
693
+ needleLength?: FgConfigNeedleLength;
694
+ beep?: boolean;
695
+ region?: FgConfigRegion;
696
+ /** `0-500` minutes, `0` = disabled */
697
+ powerSave?: number;
698
+ pressureDisplay?: FgConfigPressureDisplay;
699
+ fwUpdate?: boolean;
700
+ temperatureUnit?: FgConfigTemperatureUnit;
701
+ }
702
+ type FgTpmsConfigProtocol = 'pwmTirecheck' | 'vdaTirecheck' | 'pwmWabco' | 'vdaLdl' | 'vdaContinental' | 'vdaSchrader';
703
+ interface FgTpmsConfigProtocolSetting {
704
+ id: FgTpmsConfigProtocol;
705
+ /** Time in seconds */
706
+ time: number;
707
+ }
708
+ interface FgTpmsConfig {
709
+ protocols: FgTpmsConfigProtocolSetting[];
710
+ }
683
711
  declare class BridgeTcVehicleAxle {
684
712
  targetPressure?: number;
685
713
  tyresCount: number;
@@ -818,6 +846,7 @@ interface BleBridgeSimulated extends BleBridge {
818
846
  interface BleFlexiGaugeTpmsSimulated extends BleFlexiGaugeTpms {
819
847
  isDisabled?: boolean;
820
848
  simulatorData: {
849
+ fwVersion: string;
821
850
  battery: number;
822
851
  events: {
823
852
  /** 'L' | 'R' | 'C' | 'T' | 'B' */
@@ -826,6 +855,8 @@ interface BleFlexiGaugeTpmsSimulated extends BleFlexiGaugeTpms {
826
855
  /** In mm */
827
856
  'fg:treadDepth': number;
828
857
  };
858
+ config: FgConfig;
859
+ tpmsConfig: FgTpmsConfig;
829
860
  };
830
861
  }
831
862
  type Simulator<Service extends Record<string, any>, SimulatedDeviceType extends BleDeviceSimulated> = Service & {
@@ -951,6 +982,10 @@ declare function createTirecheckDeviceSdk(platform: DevicePlatform, bleImplement
951
982
  startTpmsScan: (deviceId: string) => void;
952
983
  setSensorDisplayId: (deviceId: string, oldSensorId: string, newSensorId: string) => Promise<void>;
953
984
  resetSensorDisplayId: (deviceId: string, sensorId: string) => Promise<void>;
985
+ getConfig: (deviceId: string) => Promise<FgConfig>;
986
+ setConfig: (deviceId: string, configJson: FgConfig) => Promise<void>;
987
+ getTpmsConfig: (deviceId: string) => Promise<FgTpmsConfig>;
988
+ setTpmsConfig: (deviceId: string, configJson: FgTpmsConfig) => Promise<void>;
954
989
  };
955
990
  flexiGauge: {
956
991
  connect(deviceId: string): Promise<void>;
@@ -994,4 +1029,4 @@ declare function createTirecheckDeviceSdk(platform: DevicePlatform, bleImplement
994
1029
  };
995
1030
  };
996
1031
 
997
- export { type BleBridge, type BleBridgeAdvertisingData, type BleBridgeOta, type BleBridgeSimulated, type BleDevice, type BleDeviceBase, type BleDeviceSimulated, type BleDeviceStatus, type BleDeviceType, type BleFlexiGauge, type BleFlexiGaugeTpms, type BleFlexiGaugeTpmsSimulated, type BlePressureStick, type BleSecurityKeys, type BleTorqueWrench, type BridgeAccessLevel, type BridgeAutolearnStatus, type BridgeCommandStructure, type BridgeCommandStructureProperties, type BridgeCommandStructurized, type BridgeConfiguration, type BridgeReading, type BridgeTcIssue, type BridgeTcTyre, type BridgeTcVehicle, BridgeTcVehicleAxle, type DeepPartial, type DevicePlatform, type DeviceState, type EventHandlers, type EventName, type FgSensorReading, type PositionInfo, type ReportStatusFn, type Simulator, type StateReason, type TorqueWrenchProperties, type TorqueWrenchReading, type TorqueWrenchStartJobParams, type Wrapper, createTirecheckDeviceSdk };
1032
+ export { type BleBridge, type BleBridgeAdvertisingData, type BleBridgeOta, type BleBridgeSimulated, type BleDevice, type BleDeviceBase, type BleDeviceSimulated, type BleDeviceStatus, type BleDeviceType, type BleFlexiGauge, type BleFlexiGaugeTpms, type BleFlexiGaugeTpmsSimulated, type BlePressureStick, type BleSecurityKeys, type BleTorqueWrench, type BridgeAccessLevel, type BridgeAutolearnStatus, type BridgeCommandStructure, type BridgeCommandStructureProperties, type BridgeCommandStructurized, type BridgeConfiguration, type BridgeReading, type BridgeTcIssue, type BridgeTcTyre, type BridgeTcVehicle, BridgeTcVehicleAxle, type DeepPartial, type DevicePlatform, type DeviceState, type EventHandlers, type EventName, type FgConfig, type FgConfigNeedleLength, type FgConfigPressureDisplay, type FgConfigPressureUnit, type FgConfigRegion, type FgConfigTdUnit, type FgConfigTemperatureUnit, type FgSensorReading, type FgTpmsConfig, type FgTpmsConfigProtocol, type FgTpmsConfigProtocolSetting, type PositionInfo, type ReportStatusFn, type Simulator, type StateReason, type TorqueWrenchProperties, type TorqueWrenchReading, type TorqueWrenchStartJobParams, type Wrapper, createTirecheckDeviceSdk };
package/dist/index.d.mts CHANGED
@@ -680,6 +680,34 @@ interface FgSensorReading {
680
680
  T: number;
681
681
  S?: string;
682
682
  }
683
+ type FgConfigPressureUnit = 'bar' | 'psi' | 'kpa';
684
+ type FgConfigTdUnit = 'mm' | 'thirtySecondOfInch';
685
+ type FgConfigNeedleLength = 15 | 30 | 40 | 100 | 130;
686
+ type FgConfigRegion = 'eu' | 'us';
687
+ type FgConfigPressureDisplay = 'measured' | 'compensated';
688
+ type FgConfigTemperatureUnit = 'fahrenheit' | 'celsius';
689
+ interface FgConfig {
690
+ menuLock?: boolean;
691
+ pressureUnit?: FgConfigPressureUnit;
692
+ tdUnit?: FgConfigTdUnit;
693
+ needleLength?: FgConfigNeedleLength;
694
+ beep?: boolean;
695
+ region?: FgConfigRegion;
696
+ /** `0-500` minutes, `0` = disabled */
697
+ powerSave?: number;
698
+ pressureDisplay?: FgConfigPressureDisplay;
699
+ fwUpdate?: boolean;
700
+ temperatureUnit?: FgConfigTemperatureUnit;
701
+ }
702
+ type FgTpmsConfigProtocol = 'pwmTirecheck' | 'vdaTirecheck' | 'pwmWabco' | 'vdaLdl' | 'vdaContinental' | 'vdaSchrader';
703
+ interface FgTpmsConfigProtocolSetting {
704
+ id: FgTpmsConfigProtocol;
705
+ /** Time in seconds */
706
+ time: number;
707
+ }
708
+ interface FgTpmsConfig {
709
+ protocols: FgTpmsConfigProtocolSetting[];
710
+ }
683
711
  declare class BridgeTcVehicleAxle {
684
712
  targetPressure?: number;
685
713
  tyresCount: number;
@@ -818,6 +846,7 @@ interface BleBridgeSimulated extends BleBridge {
818
846
  interface BleFlexiGaugeTpmsSimulated extends BleFlexiGaugeTpms {
819
847
  isDisabled?: boolean;
820
848
  simulatorData: {
849
+ fwVersion: string;
821
850
  battery: number;
822
851
  events: {
823
852
  /** 'L' | 'R' | 'C' | 'T' | 'B' */
@@ -826,6 +855,8 @@ interface BleFlexiGaugeTpmsSimulated extends BleFlexiGaugeTpms {
826
855
  /** In mm */
827
856
  'fg:treadDepth': number;
828
857
  };
858
+ config: FgConfig;
859
+ tpmsConfig: FgTpmsConfig;
829
860
  };
830
861
  }
831
862
  type Simulator<Service extends Record<string, any>, SimulatedDeviceType extends BleDeviceSimulated> = Service & {
@@ -951,6 +982,10 @@ declare function createTirecheckDeviceSdk(platform: DevicePlatform, bleImplement
951
982
  startTpmsScan: (deviceId: string) => void;
952
983
  setSensorDisplayId: (deviceId: string, oldSensorId: string, newSensorId: string) => Promise<void>;
953
984
  resetSensorDisplayId: (deviceId: string, sensorId: string) => Promise<void>;
985
+ getConfig: (deviceId: string) => Promise<FgConfig>;
986
+ setConfig: (deviceId: string, configJson: FgConfig) => Promise<void>;
987
+ getTpmsConfig: (deviceId: string) => Promise<FgTpmsConfig>;
988
+ setTpmsConfig: (deviceId: string, configJson: FgTpmsConfig) => Promise<void>;
954
989
  };
955
990
  flexiGauge: {
956
991
  connect(deviceId: string): Promise<void>;
@@ -994,4 +1029,4 @@ declare function createTirecheckDeviceSdk(platform: DevicePlatform, bleImplement
994
1029
  };
995
1030
  };
996
1031
 
997
- export { type BleBridge, type BleBridgeAdvertisingData, type BleBridgeOta, type BleBridgeSimulated, type BleDevice, type BleDeviceBase, type BleDeviceSimulated, type BleDeviceStatus, type BleDeviceType, type BleFlexiGauge, type BleFlexiGaugeTpms, type BleFlexiGaugeTpmsSimulated, type BlePressureStick, type BleSecurityKeys, type BleTorqueWrench, type BridgeAccessLevel, type BridgeAutolearnStatus, type BridgeCommandStructure, type BridgeCommandStructureProperties, type BridgeCommandStructurized, type BridgeConfiguration, type BridgeReading, type BridgeTcIssue, type BridgeTcTyre, type BridgeTcVehicle, BridgeTcVehicleAxle, type DeepPartial, type DevicePlatform, type DeviceState, type EventHandlers, type EventName, type FgSensorReading, type PositionInfo, type ReportStatusFn, type Simulator, type StateReason, type TorqueWrenchProperties, type TorqueWrenchReading, type TorqueWrenchStartJobParams, type Wrapper, createTirecheckDeviceSdk };
1032
+ export { type BleBridge, type BleBridgeAdvertisingData, type BleBridgeOta, type BleBridgeSimulated, type BleDevice, type BleDeviceBase, type BleDeviceSimulated, type BleDeviceStatus, type BleDeviceType, type BleFlexiGauge, type BleFlexiGaugeTpms, type BleFlexiGaugeTpmsSimulated, type BlePressureStick, type BleSecurityKeys, type BleTorqueWrench, type BridgeAccessLevel, type BridgeAutolearnStatus, type BridgeCommandStructure, type BridgeCommandStructureProperties, type BridgeCommandStructurized, type BridgeConfiguration, type BridgeReading, type BridgeTcIssue, type BridgeTcTyre, type BridgeTcVehicle, BridgeTcVehicleAxle, type DeepPartial, type DevicePlatform, type DeviceState, type EventHandlers, type EventName, type FgConfig, type FgConfigNeedleLength, type FgConfigPressureDisplay, type FgConfigPressureUnit, type FgConfigRegion, type FgConfigTdUnit, type FgConfigTemperatureUnit, type FgSensorReading, type FgTpmsConfig, type FgTpmsConfigProtocol, type FgTpmsConfigProtocolSetting, type PositionInfo, type ReportStatusFn, type Simulator, type StateReason, type TorqueWrenchProperties, type TorqueWrenchReading, type TorqueWrenchStartJobParams, type Wrapper, createTirecheckDeviceSdk };
package/dist/index.d.ts CHANGED
@@ -680,6 +680,34 @@ interface FgSensorReading {
680
680
  T: number;
681
681
  S?: string;
682
682
  }
683
+ type FgConfigPressureUnit = 'bar' | 'psi' | 'kpa';
684
+ type FgConfigTdUnit = 'mm' | 'thirtySecondOfInch';
685
+ type FgConfigNeedleLength = 15 | 30 | 40 | 100 | 130;
686
+ type FgConfigRegion = 'eu' | 'us';
687
+ type FgConfigPressureDisplay = 'measured' | 'compensated';
688
+ type FgConfigTemperatureUnit = 'fahrenheit' | 'celsius';
689
+ interface FgConfig {
690
+ menuLock?: boolean;
691
+ pressureUnit?: FgConfigPressureUnit;
692
+ tdUnit?: FgConfigTdUnit;
693
+ needleLength?: FgConfigNeedleLength;
694
+ beep?: boolean;
695
+ region?: FgConfigRegion;
696
+ /** `0-500` minutes, `0` = disabled */
697
+ powerSave?: number;
698
+ pressureDisplay?: FgConfigPressureDisplay;
699
+ fwUpdate?: boolean;
700
+ temperatureUnit?: FgConfigTemperatureUnit;
701
+ }
702
+ type FgTpmsConfigProtocol = 'pwmTirecheck' | 'vdaTirecheck' | 'pwmWabco' | 'vdaLdl' | 'vdaContinental' | 'vdaSchrader';
703
+ interface FgTpmsConfigProtocolSetting {
704
+ id: FgTpmsConfigProtocol;
705
+ /** Time in seconds */
706
+ time: number;
707
+ }
708
+ interface FgTpmsConfig {
709
+ protocols: FgTpmsConfigProtocolSetting[];
710
+ }
683
711
  declare class BridgeTcVehicleAxle {
684
712
  targetPressure?: number;
685
713
  tyresCount: number;
@@ -818,6 +846,7 @@ interface BleBridgeSimulated extends BleBridge {
818
846
  interface BleFlexiGaugeTpmsSimulated extends BleFlexiGaugeTpms {
819
847
  isDisabled?: boolean;
820
848
  simulatorData: {
849
+ fwVersion: string;
821
850
  battery: number;
822
851
  events: {
823
852
  /** 'L' | 'R' | 'C' | 'T' | 'B' */
@@ -826,6 +855,8 @@ interface BleFlexiGaugeTpmsSimulated extends BleFlexiGaugeTpms {
826
855
  /** In mm */
827
856
  'fg:treadDepth': number;
828
857
  };
858
+ config: FgConfig;
859
+ tpmsConfig: FgTpmsConfig;
829
860
  };
830
861
  }
831
862
  type Simulator<Service extends Record<string, any>, SimulatedDeviceType extends BleDeviceSimulated> = Service & {
@@ -951,6 +982,10 @@ declare function createTirecheckDeviceSdk(platform: DevicePlatform, bleImplement
951
982
  startTpmsScan: (deviceId: string) => void;
952
983
  setSensorDisplayId: (deviceId: string, oldSensorId: string, newSensorId: string) => Promise<void>;
953
984
  resetSensorDisplayId: (deviceId: string, sensorId: string) => Promise<void>;
985
+ getConfig: (deviceId: string) => Promise<FgConfig>;
986
+ setConfig: (deviceId: string, configJson: FgConfig) => Promise<void>;
987
+ getTpmsConfig: (deviceId: string) => Promise<FgTpmsConfig>;
988
+ setTpmsConfig: (deviceId: string, configJson: FgTpmsConfig) => Promise<void>;
954
989
  };
955
990
  flexiGauge: {
956
991
  connect(deviceId: string): Promise<void>;
@@ -994,4 +1029,4 @@ declare function createTirecheckDeviceSdk(platform: DevicePlatform, bleImplement
994
1029
  };
995
1030
  };
996
1031
 
997
- export { type BleBridge, type BleBridgeAdvertisingData, type BleBridgeOta, type BleBridgeSimulated, type BleDevice, type BleDeviceBase, type BleDeviceSimulated, type BleDeviceStatus, type BleDeviceType, type BleFlexiGauge, type BleFlexiGaugeTpms, type BleFlexiGaugeTpmsSimulated, type BlePressureStick, type BleSecurityKeys, type BleTorqueWrench, type BridgeAccessLevel, type BridgeAutolearnStatus, type BridgeCommandStructure, type BridgeCommandStructureProperties, type BridgeCommandStructurized, type BridgeConfiguration, type BridgeReading, type BridgeTcIssue, type BridgeTcTyre, type BridgeTcVehicle, BridgeTcVehicleAxle, type DeepPartial, type DevicePlatform, type DeviceState, type EventHandlers, type EventName, type FgSensorReading, type PositionInfo, type ReportStatusFn, type Simulator, type StateReason, type TorqueWrenchProperties, type TorqueWrenchReading, type TorqueWrenchStartJobParams, type Wrapper, createTirecheckDeviceSdk };
1032
+ export { type BleBridge, type BleBridgeAdvertisingData, type BleBridgeOta, type BleBridgeSimulated, type BleDevice, type BleDeviceBase, type BleDeviceSimulated, type BleDeviceStatus, type BleDeviceType, type BleFlexiGauge, type BleFlexiGaugeTpms, type BleFlexiGaugeTpmsSimulated, type BlePressureStick, type BleSecurityKeys, type BleTorqueWrench, type BridgeAccessLevel, type BridgeAutolearnStatus, type BridgeCommandStructure, type BridgeCommandStructureProperties, type BridgeCommandStructurized, type BridgeConfiguration, type BridgeReading, type BridgeTcIssue, type BridgeTcTyre, type BridgeTcVehicle, BridgeTcVehicleAxle, type DeepPartial, type DevicePlatform, type DeviceState, type EventHandlers, type EventName, type FgConfig, type FgConfigNeedleLength, type FgConfigPressureDisplay, type FgConfigPressureUnit, type FgConfigRegion, type FgConfigTdUnit, type FgConfigTemperatureUnit, type FgSensorReading, type FgTpmsConfig, type FgTpmsConfigProtocol, type FgTpmsConfigProtocolSetting, type PositionInfo, type ReportStatusFn, type Simulator, type StateReason, type TorqueWrenchProperties, type TorqueWrenchReading, type TorqueWrenchStartJobParams, type Wrapper, createTirecheckDeviceSdk };
package/dist/index.mjs CHANGED
@@ -110,12 +110,16 @@ function decimalToHex(decimal, padStart = 2) {
110
110
  const hex = decimal.toString(16).toUpperCase();
111
111
  return hex.padStart(padStart, "0");
112
112
  }
113
+ function removeEndLine(string) {
114
+ return string.replace("\r\n", "");
115
+ }
113
116
  const toolsSvc = {
114
117
  delay,
115
118
  setIntervalImmediate,
116
119
  waitUntil,
117
120
  withTimeout,
118
- canCommunicateWith
121
+ canCommunicateWith,
122
+ removeEndLine
119
123
  };
120
124
  function normalizeError(error) {
121
125
  if (!error) return new Error("Operation timed out");
@@ -185,7 +189,7 @@ const bridgeTools = {
185
189
  decodeData(data, displayUnits) {
186
190
  const _data = _.clone(data);
187
191
  if (displayUnits === "ascii") {
188
- return _data.filter((d) => d).map((x) => String.fromCharCode(x)).join("");
192
+ return String.fromCharCode(..._data.filter((d) => d));
189
193
  }
190
194
  const _reversedData = _data.reverse();
191
195
  if (displayUnits === "decimal") {
@@ -2839,7 +2843,7 @@ const flexiGaugeTpmsService$1 = {
2839
2843
  icon: "icon-flexigauge",
2840
2844
  processMessage(deviceId, message) {
2841
2845
  const numberArray = Array.from(new Uint8Array(message));
2842
- const stringFromBytes = String.fromCharCode.apply(null, numberArray).replace("\r\n", "");
2846
+ const stringFromBytes = removeEndLine(String.fromCharCode(...numberArray));
2843
2847
  for (const capability of capabilities$2) {
2844
2848
  if (capability.regex.test(stringFromBytes)) {
2845
2849
  return capability.processFn(deviceId, stringFromBytes);
@@ -2912,8 +2916,8 @@ const promiseQueue = {
2912
2916
  chunks.push(payload.slice(i, i + mtu));
2913
2917
  }
2914
2918
  for (const chunk of chunks) {
2915
- const convertedChunk = new Uint8Array(chunk).buffer;
2916
- bluetooth.write(deviceId, communication.serviceId, communication.characteristicId, convertedChunk);
2919
+ const convertedChunk = new Uint8Array(chunk);
2920
+ bluetooth.write(deviceId, communication.serviceId, communication.characteristicId, convertedChunk.buffer);
2917
2921
  }
2918
2922
  });
2919
2923
  return withTimeout(promise, 5e3, `Command timed out ${deviceResponseIdentifier[deviceId]}, ${deviceId}`);
@@ -2997,8 +3001,28 @@ const flexiGaugeTpmsCommands = {
2997
3001
  vdaRequestCommandsAccess,
2998
3002
  vdaRequestWriteAccess,
2999
3003
  processMessage,
3000
- getFirmwareVersion: getFirmwareVersion$1
3004
+ getFirmwareVersion: getFirmwareVersion$2,
3005
+ getConfig,
3006
+ setConfig,
3007
+ getTpmsConfig,
3008
+ setTpmsConfig
3001
3009
  };
3010
+ async function getConfig(deviceId) {
3011
+ const msg = "*CONFIG?";
3012
+ return sendCommand(deviceId, msg);
3013
+ }
3014
+ async function setConfig(deviceId, config) {
3015
+ const msg = `*CONFIG ${config}`;
3016
+ return sendCommand(deviceId, msg);
3017
+ }
3018
+ async function getTpmsConfig(deviceId) {
3019
+ const msg = "*TPMS CONFIG?";
3020
+ return sendCommand(deviceId, msg);
3021
+ }
3022
+ async function setTpmsConfig(deviceId, config) {
3023
+ const msg = `*TPMS CONFIG ${config}`;
3024
+ return sendCommand(deviceId, msg);
3025
+ }
3002
3026
  async function startTpmsScan(deviceId) {
3003
3027
  if (!canCommunicateWith(deviceId)) throw new Error("Flexi Gauge not connected");
3004
3028
  const scanMsg = stringToArrayBuffer("*TPMS ON TIME=10\r\n");
@@ -3018,9 +3042,9 @@ async function getBattery(deviceId) {
3018
3042
  const battery = Array.from(new Uint8Array(batteryValue))[0];
3019
3043
  return battery;
3020
3044
  }
3021
- async function getFirmwareVersion$1(deviceId) {
3045
+ async function getFirmwareVersion$2(deviceId) {
3022
3046
  const msg = "*FWVER?";
3023
- return sendCommand(deviceId, msg, "FWV");
3047
+ return sendCommand(deviceId, msg);
3024
3048
  }
3025
3049
  async function vdaRequestSensor(deviceId) {
3026
3050
  const scanMsg = "*VDA_LF_SEND SID=A0 LID=1A";
@@ -3060,9 +3084,10 @@ async function vdaSendConfiguration(deviceId, configuration) {
3060
3084
  const scanMsg = `*VDA_LF_SEND SID=A0 LID=EA PAR=17 DATA=${configuration}${crc}`;
3061
3085
  return sendCommand(deviceId, scanMsg);
3062
3086
  }
3063
- async function sendCommand(deviceId, command, identifier = "VDA") {
3087
+ async function sendCommand(deviceId, command) {
3064
3088
  const message = `${command}\r
3065
3089
  `;
3090
+ const identifier = getIdentifier$1(message);
3066
3091
  const decimalArray = stringToDecimalArray(message);
3067
3092
  let result;
3068
3093
  try {
@@ -3071,7 +3096,7 @@ async function sendCommand(deviceId, command, identifier = "VDA") {
3071
3096
  flexiGaugeTpms.disconnect(deviceId, "lostConnection");
3072
3097
  throw error;
3073
3098
  }
3074
- return result.map((num) => String.fromCharCode(num)).join("");
3099
+ return String.fromCharCode(...result);
3075
3100
  }
3076
3101
  function processMessage(deviceId, payload) {
3077
3102
  promiseQueue.processMessage(deviceId, payload, getIdentifier$1, isComplete$1, isError$1);
@@ -3084,13 +3109,163 @@ function isError$1(message) {
3084
3109
  return false;
3085
3110
  }
3086
3111
  function getIdentifier$1(message) {
3087
- const messageString = message.map((num) => String.fromCharCode(num)).join("");
3088
- return messageString.slice(0, 3);
3112
+ const messageString = typeof message === "string" ? message : String.fromCharCode(...message);
3113
+ const [, commands] = removeEndLine(messageString).match(/\*?([^=|^?]+)/) || [];
3114
+ const [identifier, subIdentifier] = commands.split(" ") || [];
3115
+ return identifier === "TPMS" && subIdentifier ? `${identifier} ${subIdentifier}` : identifier;
3089
3116
  }
3090
3117
  function getReversedSensorId(sensorId) {
3091
3118
  return sensorId.match(/.{1,2}/g).reverse().join("");
3092
3119
  }
3093
3120
 
3121
+ const fgConfigPressureUnits = ["bar", "psi", "kpa"];
3122
+ const fgConfigTdUnits = ["mm", "thirtySecondOfInch"];
3123
+ const fgConfigNeedleLengths = [15, 30, 40, 100, 130];
3124
+ const fgConfigRegions = ["eu", "us"];
3125
+ const fgConfigPressureDisplay = ["measured", "compensated"];
3126
+ const fgConfigTemperatureUnits = ["fahrenheit", "celsius"];
3127
+ const configToJsonMapping = {
3128
+ ML: (v) => ({ menuLock: v === "1" }),
3129
+ PU: (v) => ({ pressureUnit: fgConfigPressureUnits[Number(v)] }),
3130
+ TU: (v) => ({ tdUnit: fgConfigTdUnits[Number(v)] }),
3131
+ NL: (v) => ({ needleLength: fgConfigNeedleLengths[Number(v)] }),
3132
+ BE: (v) => ({ beep: v === "1" }),
3133
+ RS: (v) => ({ region: fgConfigRegions[Number(v)] }),
3134
+ PS: (v) => ({ powerSave: Number(v) }),
3135
+ PD: (v) => ({ pressureDisplay: fgConfigPressureDisplay[Number(v)] }),
3136
+ FW: (v) => ({ fwUpdate: v === "1" }),
3137
+ TT: (v) => ({ temperatureUnit: fgConfigTemperatureUnits[Number(v)] })
3138
+ };
3139
+ const configFromJsonMapping = {
3140
+ menuLock: (v) => `ML=${v ? "1" : "0"}`,
3141
+ pressureUnit: (v) => `PU=${getIndex(fgConfigPressureUnits, v)}`,
3142
+ tdUnit: (v) => `TU=${getIndex(fgConfigTdUnits, v)}`,
3143
+ needleLength: (v) => `NL=${getIndex(fgConfigNeedleLengths, v)}`,
3144
+ beep: (v) => `BE=${v ? "1" : "0"}`,
3145
+ region: (v) => `RS=${getIndex(fgConfigRegions, v)}`,
3146
+ powerSave: (v) => `PS=${v.toString()}`,
3147
+ pressureDisplay: (v) => `PD=${getIndex(fgConfigPressureDisplay, v)}`,
3148
+ fwUpdate: (v) => `FW=${v ? "1" : "0"}`,
3149
+ temperatureUnit: (v) => `TT=${getIndex(fgConfigTemperatureUnits, v)}`
3150
+ };
3151
+ const fgTpmsConfigProtocols = [
3152
+ "pwmTirecheck",
3153
+ "vdaTirecheck",
3154
+ "pwmWabco",
3155
+ "vdaLdl",
3156
+ "vdaContinental",
3157
+ "vdaSchrader"
3158
+ ];
3159
+ const flexiGaugeTpmsServiceMapping = {
3160
+ getDefaultConfigJson() {
3161
+ return {
3162
+ menuLock: false,
3163
+ pressureUnit: "bar",
3164
+ tdUnit: "mm",
3165
+ needleLength: 40,
3166
+ beep: true,
3167
+ region: "eu",
3168
+ powerSave: 15,
3169
+ pressureDisplay: "measured",
3170
+ fwUpdate: false,
3171
+ temperatureUnit: "fahrenheit"
3172
+ };
3173
+ },
3174
+ getDefaultTpmsConfigJson() {
3175
+ return _.cloneDeep({
3176
+ protocols: fgTpmsConfigProtocols.map((id) => ({ id, time: 5 }))
3177
+ });
3178
+ },
3179
+ /**
3180
+ * @param configString example: `CONFIG ML=0 PU=0 TU=0 NL=2 BE=1 RS=0 PS=15 PD=0 FW=0 TT=0\r\n`
3181
+ */
3182
+ getConfigJson(configString) {
3183
+ let config = this.getDefaultConfigJson();
3184
+ const parts = toolsSvc.removeEndLine(configString).split(" ").slice(1).map((v) => v.split("="));
3185
+ for (const [name, value] of parts) {
3186
+ const toJsonFn = configToJsonMapping[name];
3187
+ config = { ...config, ...toJsonFn(value) };
3188
+ }
3189
+ return config;
3190
+ },
3191
+ /**
3192
+ * @returns example: `ML=0 PU=0 TU=0 NL=2 BE=1 RS=0 PS=15 PD=0 FW=0 TT=0`
3193
+ */
3194
+ getConfig(configJson) {
3195
+ const configString = Object.entries(configJson).map(([key, value]) => {
3196
+ const fromJsonFn = configFromJsonMapping[key];
3197
+ return fromJsonFn(value);
3198
+ }).join(" ");
3199
+ return configString;
3200
+ },
3201
+ /**
3202
+ * @param configString example: `TPMS CONFIG ALG=0001020304050000 TIME=5050505050500000\r\n`
3203
+ *
3204
+ * - every protocol/time is 2 bytes
3205
+ * - we only support 6 protocols, rest is empty
3206
+ * - time is stored as n*100ms (`50` = 50x100ms = 5s)
3207
+ * - protocols can be duplicate when set in Fg Menu
3208
+ *
3209
+ * protocol codes:
3210
+ * - `00` - PWM Tirecheck - `pwmTirecheck`
3211
+ * - `01` - VDA Tirecheck - `vdaTirecheck`
3212
+ * - `02` - PWM WABCO - `pwmWabco`
3213
+ * - `03` - VDA LDL - `vdaLdl`
3214
+ * - `04` - VDA Continental - `vdaContinental`
3215
+ * - `05` - VDA Schrader - `vdaSchrader`
3216
+ */
3217
+ getTpmsConfigJson(configString) {
3218
+ const config = this.getDefaultTpmsConfigJson();
3219
+ const parts = toolsSvc.removeEndLine(configString).split(" ").slice(2).map((v) => {
3220
+ const [name, value] = v.split("=");
3221
+ if (name === "ALG") return ["protocols", getTpmsConfigProtocolList(value)];
3222
+ if (name === "TIME") return ["times", getTpmsConfigProtocolTimeList(value)];
3223
+ throw new Error(`Unknown Fg Tpms config part: ${name}`);
3224
+ });
3225
+ const pairs = _.fromPairs(parts);
3226
+ const zipped = _.zipObject(pairs.protocols, pairs.times);
3227
+ if (pairs.protocols.length) config.protocols = [];
3228
+ for (const [protocol, time] of Object.entries(zipped)) {
3229
+ config.protocols.push({ id: protocol, time: time / 10 });
3230
+ }
3231
+ if (config.protocols.length < 6) {
3232
+ const missingProtocols = fgTpmsConfigProtocols.filter((id) => !config.protocols.find((p) => p.id === id));
3233
+ for (const protocol of missingProtocols) {
3234
+ config.protocols.push({ id: protocol, time: 0 });
3235
+ }
3236
+ }
3237
+ return config;
3238
+ },
3239
+ /**
3240
+ * @returns example: `ALG=0001020304050000 TIME=5050505050500000`
3241
+ */
3242
+ getTpmsConfig(tpmsConfigJson) {
3243
+ const unzipped = tpmsConfigJson.protocols.reduce(
3244
+ (acc, { id, time }) => {
3245
+ acc.protocols.push(getIndex(fgTpmsConfigProtocols, id).padStart(2, "0"));
3246
+ if (time >= 10) throw new Error(`Fg Tpms config time value out of range: ${time}`);
3247
+ acc.times.push(
3248
+ Math.round(time * 10).toString().padStart(2, "0")
3249
+ );
3250
+ return acc;
3251
+ },
3252
+ { protocols: [], times: [] }
3253
+ );
3254
+ const protocols = unzipped.protocols.join("").padEnd(16, "0");
3255
+ const times = unzipped.times.join("").padEnd(16, "0");
3256
+ return `ALG=${protocols} TIME=${times}`;
3257
+ }
3258
+ };
3259
+ function getIndex(array, value) {
3260
+ return array.indexOf(value).toString();
3261
+ }
3262
+ function getTpmsConfigProtocolList(configString) {
3263
+ return configString.match(/(\d{2})/g)?.slice(0, 6).map((v) => fgTpmsConfigProtocols[Number.parseInt(v)]);
3264
+ }
3265
+ function getTpmsConfigProtocolTimeList(configString) {
3266
+ return configString.match(/(\d{2})/g)?.slice(0, 6).map((v) => Number.parseInt(v));
3267
+ }
3268
+
3094
3269
  let sensorReadings = [];
3095
3270
  const capabilities$1 = [
3096
3271
  {
@@ -3107,7 +3282,7 @@ const capabilities$1 = [
3107
3282
  {
3108
3283
  id: "tpms",
3109
3284
  processFn: processTpms,
3110
- regex: /^\*?TPMS.*/
3285
+ regex: /^\*?TPMS (ISON|DATA|ENDSCANNING).*/
3111
3286
  }
3112
3287
  ];
3113
3288
  const flexiGaugeTpmsService = {
@@ -3129,7 +3304,7 @@ const flexiGaugeTpmsService = {
3129
3304
  icon: "icon-flexigauge",
3130
3305
  processMessage(deviceId, message) {
3131
3306
  const numberArray = Array.from(new Uint8Array(message));
3132
- const stringFromBytes = String.fromCharCode.apply(null, numberArray).replace("\r\n", "");
3307
+ const stringFromBytes = removeEndLine(String.fromCharCode(...numberArray));
3133
3308
  for (const capability of capabilities$1) {
3134
3309
  if (capability.regex.test(stringFromBytes)) {
3135
3310
  return capability.processFn(deviceId, stringFromBytes);
@@ -3137,8 +3312,28 @@ const flexiGaugeTpmsService = {
3137
3312
  }
3138
3313
  flexiGaugeTpmsCommands.processMessage(deviceId, message);
3139
3314
  },
3315
+ async getConfig(deviceId) {
3316
+ const response = await flexiGaugeTpmsCommands.getConfig(deviceId);
3317
+ return flexiGaugeTpmsServiceMapping.getConfigJson(response);
3318
+ },
3319
+ async setConfig(deviceId, configJson) {
3320
+ const fw = await getFirmwareVersion$1(deviceId);
3321
+ if (fw < "1.4.4") throw new Error("Config change is not supported on this firmware version");
3322
+ const config = flexiGaugeTpmsServiceMapping.getConfig(configJson);
3323
+ await flexiGaugeTpmsCommands.setConfig(deviceId, config);
3324
+ },
3325
+ async getTpmsConfig(deviceId) {
3326
+ const response = await flexiGaugeTpmsCommands.getTpmsConfig(deviceId);
3327
+ return flexiGaugeTpmsServiceMapping.getTpmsConfigJson(response);
3328
+ },
3329
+ async setTpmsConfig(deviceId, configJson) {
3330
+ const fw = await getFirmwareVersion$1(deviceId);
3331
+ if (fw < "1.4.4") throw new Error("TPMS Config change is not supported on this firmware version");
3332
+ const config = flexiGaugeTpmsServiceMapping.getTpmsConfig(configJson);
3333
+ await flexiGaugeTpmsCommands.setTpmsConfig(deviceId, config);
3334
+ },
3140
3335
  setSensorDisplayId,
3141
- getFirmwareVersion
3336
+ getFirmwareVersion: getFirmwareVersion$1
3142
3337
  };
3143
3338
  function processTreadDepth(deviceId, value) {
3144
3339
  const treadDepth = value.match(/\d+/)?.[0];
@@ -3178,7 +3373,7 @@ function processTpms(deviceId, value) {
3178
3373
  }
3179
3374
  }
3180
3375
  async function setSensorDisplayId(deviceId, oldSensorId, newSensorId) {
3181
- const fw = await getFirmwareVersion(deviceId);
3376
+ const fw = await getFirmwareVersion$1(deviceId);
3182
3377
  if (fw < "1.4.4") throw new Error("Programming sensors is not supported on this firmware version");
3183
3378
  await findSensor(deviceId, oldSensorId);
3184
3379
  const seed = await getSeed(deviceId, oldSensorId);
@@ -3227,10 +3422,10 @@ async function vdaRepeat(fn) {
3227
3422
  }
3228
3423
  throw new Error("VDA Timeout");
3229
3424
  }
3230
- async function getFirmwareVersion(deviceId) {
3425
+ async function getFirmwareVersion$1(deviceId) {
3231
3426
  const response = await flexiGaugeTpmsCommands.getFirmwareVersion(deviceId);
3232
- const groupedValue = response.split(" ");
3233
- return groupedValue[1];
3427
+ const [, version] = removeEndLine(response).split(" ");
3428
+ return version;
3234
3429
  }
3235
3430
 
3236
3431
  const flexiGaugeTpms = {
@@ -3260,7 +3455,11 @@ const flexiGaugeTpms = {
3260
3455
  getFirmwareVersion: flexiGaugeTpmsService.getFirmwareVersion,
3261
3456
  startTpmsScan: flexiGaugeTpmsService.startTpmsScan,
3262
3457
  setSensorDisplayId: flexiGaugeTpmsService.setSensorDisplayId,
3263
- resetSensorDisplayId: (deviceId, sensorId) => flexiGaugeTpmsService.setSensorDisplayId(deviceId, sensorId, "00000000")
3458
+ resetSensorDisplayId: (deviceId, sensorId) => flexiGaugeTpmsService.setSensorDisplayId(deviceId, sensorId, "00000000"),
3459
+ getConfig: flexiGaugeTpmsService.getConfig,
3460
+ setConfig: flexiGaugeTpmsService.setConfig,
3461
+ getTpmsConfig: flexiGaugeTpmsService.getTpmsConfig,
3462
+ setTpmsConfig: flexiGaugeTpmsService.setTpmsConfig
3264
3463
  };
3265
3464
 
3266
3465
  const capabilities = [
@@ -3276,7 +3475,7 @@ const pressureStickService = {
3276
3475
  },
3277
3476
  processMessage(deviceId, message) {
3278
3477
  const numberArray = Array.from(new Uint8Array(message));
3279
- const stringFromBytes = String.fromCharCode.apply(null, numberArray).replace("\r\n", "");
3478
+ const stringFromBytes = removeEndLine(String.fromCharCode(...numberArray));
3280
3479
  for (const capability of capabilities) {
3281
3480
  if (capability.regex.test(stringFromBytes)) {
3282
3481
  return capability.processFn(deviceId, stringFromBytes);
@@ -3376,7 +3575,7 @@ const torqueWrenchCommands = {
3376
3575
  const formattedError = formatError(error);
3377
3576
  throw new Error(formattedError);
3378
3577
  }
3379
- return result.map((num) => String.fromCharCode(num)).join("");
3578
+ return String.fromCharCode(...result);
3380
3579
  },
3381
3580
  processMessage(deviceId, payload) {
3382
3581
  promiseQueue.processMessage(deviceId, payload, getIdentifier, isComplete, isError);
@@ -3392,7 +3591,7 @@ function isError(message) {
3392
3591
  }
3393
3592
  function getIdentifier(message) {
3394
3593
  if (message[0] === 123) return "200";
3395
- return message.slice(6, 9).map((num) => String.fromCharCode(num)).join("");
3594
+ return String.fromCharCode(...message.slice(6, 9));
3396
3595
  }
3397
3596
  function getChecksum(message) {
3398
3597
  let checksum = 0;
@@ -3404,7 +3603,7 @@ function getChecksum(message) {
3404
3603
  function formatError(error) {
3405
3604
  if (!error.cause) throw error;
3406
3605
  const message = error.cause.slice(6, 8);
3407
- const errorId = message.map((num) => String.fromCharCode(num)).join("");
3606
+ const errorId = String.fromCharCode(...message);
3408
3607
  return errors[errorId] || "Unknown error";
3409
3608
  }
3410
3609
 
@@ -3940,10 +4139,45 @@ const flexiGaugeTpmsSimulator = {
3940
4139
  resetSensorDisplayId(deviceId, sensorId) {
3941
4140
  return new Promise((resolve) => resolve());
3942
4141
  },
3943
- async getFirmwareVersion(deviceId) {
4142
+ async getConfig(deviceId) {
4143
+ const fg = getSimulatedFg(deviceId);
3944
4144
  await toolsSvc.delay(100);
3945
- return "9.9.9";
4145
+ const fwVersion = await getFirmwareVersion(deviceId);
4146
+ if (fwVersion >= "1.4.4") {
4147
+ return { ...fg.simulatorData.config };
4148
+ }
4149
+ throw new Error(`Getting Fg config is not supported on firmware version ${fwVersion}, min 1.4.4 required`);
3946
4150
  },
4151
+ async setConfig(deviceId, configJson) {
4152
+ const fg = getSimulatedFg(deviceId);
4153
+ await toolsSvc.delay(100);
4154
+ const fwVersion = await getFirmwareVersion(deviceId);
4155
+ if (fwVersion >= "1.4.4") {
4156
+ fg.simulatorData.config = { ...configJson };
4157
+ return;
4158
+ }
4159
+ throw new Error(`Setting Fg config is not supported on firmware version ${fwVersion}, min 1.4.4 required`);
4160
+ },
4161
+ async getTpmsConfig(deviceId) {
4162
+ const fg = getSimulatedFg(deviceId);
4163
+ await toolsSvc.delay(100);
4164
+ const fwVersion = await getFirmwareVersion(deviceId);
4165
+ if (fwVersion >= "1.4.4") {
4166
+ return _.cloneDeep(fg.simulatorData.tpmsConfig);
4167
+ }
4168
+ throw new Error(`Getting Fg TPMS config is not supported on firmware version ${fwVersion}, min 1.4.4 required`);
4169
+ },
4170
+ async setTpmsConfig(deviceId, tpmsConfig) {
4171
+ const fg = getSimulatedFg(deviceId);
4172
+ await toolsSvc.delay(100);
4173
+ const fwVersion = await getFirmwareVersion(deviceId);
4174
+ if (fwVersion >= "1.4.4") {
4175
+ fg.simulatorData.tpmsConfig = _.cloneDeep(tpmsConfig);
4176
+ return;
4177
+ }
4178
+ throw new Error(`Setting Fg TPMS config is not supported on firmware version ${fwVersion}, min 1.4.4 required`);
4179
+ },
4180
+ getFirmwareVersion,
3947
4181
  onButtonPress(callback) {
3948
4182
  },
3949
4183
  onTpms(callback) {
@@ -3951,6 +4185,11 @@ const flexiGaugeTpmsSimulator = {
3951
4185
  onTreadDepth(callback) {
3952
4186
  }
3953
4187
  };
4188
+ async function getFirmwareVersion(deviceId) {
4189
+ const fg = getSimulatedFg(deviceId);
4190
+ await toolsSvc.delay(100);
4191
+ return fg.simulatorData.fwVersion;
4192
+ }
3954
4193
  function getSimulatedFg(deviceId) {
3955
4194
  const simulatedDevice = store.simulatedDevices[deviceId];
3956
4195
  if (!simulatedDevice) throw new Error(`Simulated bridge was not found: ${deviceId}`);
@@ -3964,12 +4203,12 @@ function getSimulatedFgTemplate() {
3964
4203
  type: "flexiGaugeTpms",
3965
4204
  rssi: -50,
3966
4205
  simulatorData: {
4206
+ fwVersion: "1.4.4",
3967
4207
  battery: 45,
3968
4208
  events: {
3969
4209
  "fg:button": "L",
3970
4210
  "fg:tpms": {
3971
- // TODO: replace with realistic values
3972
- ID: "FFFF1212",
4211
+ ID: generateEAN(268431361, 268435455),
3973
4212
  Rssi: "-50",
3974
4213
  PC: 12,
3975
4214
  T: 15,
@@ -3978,10 +4217,22 @@ function getSimulatedFgTemplate() {
3978
4217
  S: "S"
3979
4218
  },
3980
4219
  "fg:treadDepth": 5.2
3981
- }
4220
+ },
4221
+ config: flexiGaugeTpmsServiceMapping.getDefaultConfigJson(),
4222
+ tpmsConfig: flexiGaugeTpmsServiceMapping.getDefaultTpmsConfigJson()
3982
4223
  }
3983
4224
  };
3984
4225
  }
4226
+ function generateEAN(min, max) {
4227
+ const serial = Math.round(min + Math.random() * (max - min)).toString(16);
4228
+ let p1 = 0;
4229
+ for (let i = 0; i < serial.length; i++) {
4230
+ p1 += Number.parseInt(serial[i], 16) * (i % 2 === 1 ? 1 : 3);
4231
+ }
4232
+ const p2 = Math.ceil(p1 / 16) * 16;
4233
+ const check = p2 - p1;
4234
+ return `${serial}${check.toString(16)}`.toUpperCase();
4235
+ }
3985
4236
 
3986
4237
  const deviceServicesAndSimulators = [
3987
4238
  [bridge, bridgeSimulator],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tirecheck-device-sdk",
3
- "version": "0.2.45",
3
+ "version": "0.2.46",
4
4
  "description": "SDK for working with various devices produced by Tirecheck via Bluetooth (CAN Bridge, Routers, Sensors, FlexiGauge, PressureStick, etc)",
5
5
  "author": "Leonid Buneev <leonid.buneev@tirecheck.com>",
6
6
  "license": "ISC",