tirecheck-device-sdk 0.1.2 → 0.1.4

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
@@ -1,80 +1,160 @@
1
1
  'use strict';
2
2
 
3
3
  const _ = require('lodash');
4
+ const CryptoJs = require('crypto-js');
4
5
 
5
6
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
6
7
 
7
8
  const ___default = /*#__PURE__*/_interopDefaultCompat(_);
9
+ const CryptoJs__default = /*#__PURE__*/_interopDefaultCompat(CryptoJs);
8
10
 
9
11
  let ble;
10
12
  function setBleImplementation(bleImplementation) {
11
13
  ble = bleImplementation;
12
14
  }
13
15
 
16
+ const store = {
17
+ platform: "web",
18
+ devices: {},
19
+ /** unknown - no information but since we get advertisings it is most likely disconnected, connecting - trying to connect via bluetooth, connected - connected via bluetooth (can send write and read), paired - all additional connection steps completed (setMtu, sendPin, etc) */
20
+ deviceState: {},
21
+ // unknown => connecting => connected => paired => disconnecting => disconnected
22
+ bridgesReboot: {}
23
+ };
24
+
25
+ function delay(delayMs) {
26
+ return new Promise((resolve) => setTimeout(resolve, delayMs));
27
+ }
28
+ function setIntervalImmediate(fn, intervalMs) {
29
+ fn();
30
+ return setInterval(fn, intervalMs);
31
+ }
32
+ function withTimeout(promise, timeoutMs, error) {
33
+ return Promise.race([
34
+ promise,
35
+ new Promise((resolve, reject) => setTimeout(() => reject(normalizeError(error)), timeoutMs))
36
+ ]);
37
+ }
38
+ function waitUntil(callback, timeout = 5e3) {
39
+ return new Promise((resolve, reject) => {
40
+ const interval = setInterval(() => {
41
+ if (callback()) {
42
+ clearInterval(interval);
43
+ resolve();
44
+ }
45
+ }, 500);
46
+ setTimeout(() => {
47
+ clearInterval(interval);
48
+ resolve();
49
+ }, timeout);
50
+ });
51
+ }
52
+ function canCommunicateWith(deviceId) {
53
+ const state = store.deviceState[deviceId];
54
+ return ["connected", "paired"].includes(state);
55
+ }
56
+ function getDeviceData(deviceId) {
57
+ const deviceData = store.devices[deviceId];
58
+ if (!deviceData) throw new Error("Device data missing");
59
+ return deviceData;
60
+ }
61
+ function isVersionGreaterThan(deviceId, version) {
62
+ if (version?.length !== 5) {
63
+ throw new Error("Invalid version format");
64
+ }
65
+ if (store.devices[deviceId]?.advertisingData?.fwVersion) {
66
+ return store.devices[deviceId]?.advertisingData?.fwVersion > version;
67
+ }
68
+ return false;
69
+ }
70
+ function getPositionId(axlePosition, tyrePosition, axleTyresCount, isSpare) {
71
+ let i2sTyrePosition = tyrePosition;
72
+ if (tyrePosition === 4 && axleTyresCount === 2) i2sTyrePosition = 2;
73
+ if (tyrePosition != null) return axlePosition * 100 + i2sTyrePosition * 10 + (isSpare ? 0 : axleTyresCount);
74
+ if (axlePosition != null) return axlePosition;
75
+ return 0;
76
+ }
77
+ function stringToArrayBuffer(string) {
78
+ const array = new Uint8Array(string.length);
79
+ for (let i = 0, l = string.length; i < l; i++) {
80
+ array[i] = string.charCodeAt(i);
81
+ }
82
+ return array.buffer;
83
+ }
84
+ const toolsSvc = {
85
+ delay,
86
+ setIntervalImmediate,
87
+ waitUntil,
88
+ withTimeout,
89
+ canCommunicateWith,
90
+ getDeviceData
91
+ };
92
+ function normalizeError(error) {
93
+ if (!error) return new Error("Operation timed out");
94
+ if (typeof error === "string") return new Error(error);
95
+ return error;
96
+ }
97
+
98
+ const tyreLabels = getDefaultTyreLabels();
14
99
  const bridgeTools = {
15
- // convertBytesToStructure(objStructure: BridgeCommandStructureProperties, payload: number[]) {
16
- // const numArray = _.clone(payload)
17
- // const keys = Object.keys(objStructure)
18
- // const sumWithInitial = keys.reduce((accumulator, currentValue) => accumulator + objStructure[currentValue].size, 0)
19
- // if (numArray.length !== sumWithInitial) {
20
- // throw new Error('Cannot convert bytes to object')
21
- // }
22
- // const result: any = {}
23
- // for (const key of keys) {
24
- // const encodedData: number[] = numArray.splice(0, objStructure[key].size)
25
- // result[key] = this.decodeData(encodedData, objStructure[key].display)
26
- // }
27
- // return result
28
- // },
29
- // convertStructureToBytes(objStructure: any, structurizedObj: any) {
30
- // const keys = Object.keys(objStructure)
31
- // const result: number[] = []
32
- // for (const key of keys) {
33
- // const encoded = this.encodeData(structurizedObj[key], objStructure[key].display)
34
- // if (encoded.length < objStructure[key].size) {
35
- // const _padArray = Array.from({ length: objStructure[key].size - encoded.length }, () => 0)
36
- // encoded.push(..._padArray)
37
- // }
38
- // result.push(...encoded)
39
- // }
40
- // return result
41
- // },
42
- // convertAutoLearnObjectToBinary(data: Record<string, string>): Record<string, string> {
43
- // const result: Record<string, string> = {}
44
- // for (const key in data) {
45
- // if (!key.includes('axle')) continue
46
- // // Parse the value as hexadecimal (base 16)
47
- // const value = parseInt(data[key], 16)
48
- // // Convert to binary and pad with leading zeros to ensure 8 characters
49
- // const binaryString = value.toString(2).padStart(8, '0')
50
- // result[key] = binaryString
51
- // }
52
- // return result
53
- // },
54
- // decodeData(data: number[], displayUnits: undefined | 'ascii' | 'decimal') {
55
- // const _data = _.clone(data)
56
- // if (displayUnits === 'ascii') {
57
- // return _data
58
- // .filter(d => d)
59
- // .map(x => String.fromCharCode(x))
60
- // .join('')
61
- // }
62
- // const _reversedData = _data.reverse()
63
- // if (displayUnits === 'decimal') {
64
- // return Number(`0x${_reversedData.map(dec => this.decimalToHex(dec)).join('')}`.replace(/\r\n/g, ''))
65
- // }
66
- // return _reversedData.map(dec => this.decimalToHex(dec)).join('')
67
- // },
68
- // encodeData(data: number | string, displayUnits: undefined | 'ascii' | 'decimal'): number[] {
69
- // const _data = _.clone(data)
70
- // if (displayUnits === 'ascii') {
71
- // return (_data as string).split('').map((v, i) => (_data as string).charCodeAt(i))
72
- // }
73
- // if (displayUnits === 'decimal') {
74
- // return this.hexToDecimalArray(this.decimalToHex(_data as number)).reverse()
75
- // }
76
- // return this.hexToDecimalArray(_data as string).reverse()
77
- // },
100
+ convertBytesToStructure(objStructure, payload) {
101
+ const numArray = ___default.clone(payload);
102
+ const keys = Object.keys(objStructure);
103
+ const sumWithInitial = keys.reduce((accumulator, currentValue) => accumulator + objStructure[currentValue].size, 0);
104
+ if (numArray.length !== sumWithInitial) {
105
+ throw new Error("Cannot convert bytes to object");
106
+ }
107
+ const result = {};
108
+ for (const key of keys) {
109
+ const encodedData = numArray.splice(0, objStructure[key].size);
110
+ result[key] = this.decodeData(encodedData, objStructure[key].display);
111
+ }
112
+ return result;
113
+ },
114
+ convertStructureToBytes(objStructure, structurizedObj) {
115
+ const keys = Object.keys(objStructure);
116
+ const result = [];
117
+ for (const key of keys) {
118
+ const encoded = this.encodeData(structurizedObj[key], objStructure[key].display);
119
+ if (encoded.length < objStructure[key].size) {
120
+ const _padArray = Array.from({ length: objStructure[key].size - encoded.length }, () => 0);
121
+ encoded.push(..._padArray);
122
+ }
123
+ result.push(...encoded);
124
+ }
125
+ return result;
126
+ },
127
+ convertAutoLearnObjectToBinary(data) {
128
+ const result = {};
129
+ for (const key in data) {
130
+ if (!key.includes("axle")) continue;
131
+ const value = Number.parseInt(data[key], 16);
132
+ const binaryString = value.toString(2).padStart(8, "0");
133
+ result[key] = binaryString;
134
+ }
135
+ return result;
136
+ },
137
+ decodeData(data, displayUnits) {
138
+ const _data = ___default.clone(data);
139
+ if (displayUnits === "ascii") {
140
+ return _data.filter((d) => d).map((x) => String.fromCharCode(x)).join("");
141
+ }
142
+ const _reversedData = _data.reverse();
143
+ if (displayUnits === "decimal") {
144
+ return Number(`0x${_reversedData.map((dec) => this.decimalToHex(dec)).join("")}`.replace(/\r\n/g, ""));
145
+ }
146
+ return _reversedData.map((dec) => this.decimalToHex(dec)).join("");
147
+ },
148
+ encodeData(data, displayUnits) {
149
+ const _data = ___default.clone(data);
150
+ if (displayUnits === "ascii") {
151
+ return _data.split("").map((v, i) => _data.charCodeAt(i));
152
+ }
153
+ if (displayUnits === "decimal") {
154
+ return this.hexToDecimalArray(this.decimalToHex(_data)).reverse();
155
+ }
156
+ return this.hexToDecimalArray(_data).reverse();
157
+ },
78
158
  // getBridgeId(device: BluetoothDeviceBridge) {
79
159
  // // [244,177, 0, 34,123, 155] => F4B100227B9B
80
160
  // return (
@@ -85,20 +165,20 @@ const bridgeTools = {
85
165
  // .toUpperCase() || ''
86
166
  // )
87
167
  // },
88
- // pkcs(array: any[], length: number) {
89
- // const pkcsValue = length - array.length
90
- // if (pkcsValue > 0) {
91
- // return [...array, ...new Array(pkcsValue).fill(pkcsValue)]
92
- // }
93
- // return array
94
- // },
168
+ pkcs(array, length) {
169
+ const pkcsValue = length - array.length;
170
+ if (pkcsValue > 0) {
171
+ return [...array, ...new Array(pkcsValue).fill(pkcsValue)];
172
+ }
173
+ return array;
174
+ },
95
175
  decimalToHex(decimal) {
96
176
  const hex = decimal.toString(16);
97
177
  return hex.padStart(2, "0");
98
178
  },
99
- // hexToDecimalArray(hex: string): number[] {
100
- // return hex.match(/.{1,2}/g)?.map(byte => parseInt(byte, 16)) || []
101
- // },
179
+ hexToDecimalArray(hex) {
180
+ return hex.match(/.{1,2}/g)?.map((byte) => Number.parseInt(byte, 16)) || [];
181
+ },
102
182
  getFwVersion(payload) {
103
183
  if (payload?.length !== 3) {
104
184
  console.warn("Could not process FwVersion ", payload);
@@ -109,46 +189,125 @@ const bridgeTools = {
109
189
  acc.push(value[0] === "0" ? value[1] : value);
110
190
  return acc;
111
191
  }, []).join(".");
192
+ },
193
+ barToKpaByte(value) {
194
+ if (!value) return 0;
195
+ return ___default.round((value + 1) * 100 / 5.0625);
196
+ },
197
+ convertSensorIdForBridge(sensorId) {
198
+ if (sensorId.startsWith("2") && sensorId.length === 10) {
199
+ return Number.parseInt(sensorId).toString(16);
200
+ }
201
+ return sensorId;
202
+ },
203
+ kpaByteToBar(value, decrementValue = 1) {
204
+ if (!___default.isNumber(value)) {
205
+ throw new TypeError("Value has to be a number");
206
+ }
207
+ const rawBar = value * 5.0625 / 100;
208
+ return Math.max(rawBar - decrementValue, 0);
209
+ },
210
+ isVersionGreaterThan(device, version) {
211
+ if (version?.length !== 5) {
212
+ throw new Error("Invalid version format");
213
+ }
214
+ if (device.advertisingData.fwVersion) {
215
+ return device.advertisingData?.fwVersion > version;
216
+ }
217
+ return false;
218
+ },
219
+ convertSensorIdFromBridge(sensorId) {
220
+ const sensorIdDecimal = Number.parseInt(sensorId, 16).toString();
221
+ if (sensorId.startsWith("B") && sensorIdDecimal.startsWith("2") && sensorIdDecimal.length === 10) {
222
+ return sensorIdDecimal;
223
+ }
224
+ return sensorId;
225
+ },
226
+ getPositionInfo(positionId) {
227
+ if (positionId === 0)
228
+ return {
229
+ axlePosition: null,
230
+ tyrePosition: null,
231
+ name: "Vehicle"
232
+ };
233
+ if (positionId < 100)
234
+ return {
235
+ axlePosition: positionId,
236
+ tyrePosition: null,
237
+ name: `Axle ${positionId.toString()}`
238
+ };
239
+ const axleTyresCount = positionId % 10;
240
+ const axlePosition = Math.floor(positionId / 100);
241
+ const tyrePosition = Math.floor(positionId / 10) % 10;
242
+ const isSpare = String(positionId).endsWith("0");
243
+ return {
244
+ positionId,
245
+ axlePosition,
246
+ tyrePosition: isSpare ? 1 : tyrePosition,
247
+ axleTyresCount,
248
+ name: this.getPositionNameInner(axlePosition, tyrePosition, axleTyresCount),
249
+ description: this.getPositionDescriptionInner(axlePosition, tyrePosition, axleTyresCount),
250
+ isSpareTyre: axleTyresCount === 0 || isSpare,
251
+ isLeftTyre: axleTyresCount === 2 && tyrePosition === 1 || axleTyresCount === 4 && tyrePosition <= 2,
252
+ isRightTyre: axleTyresCount === 2 && tyrePosition === 2 || axleTyresCount === 4 && tyrePosition > 2,
253
+ isCenterTyre: axleTyresCount === 1,
254
+ isTwinTyre: axleTyresCount === 4,
255
+ isOuterTwinTyre: axleTyresCount === 4 && [1, 4].includes(tyrePosition),
256
+ isInnerTwinTyre: axleTyresCount === 4 && [2, 3].includes(tyrePosition)
257
+ };
258
+ },
259
+ getPositionNameInner(axlePosition, tyrePosition, axleTyresCount) {
260
+ const l = tyreLabels[tyrePosition * 10 + axleTyresCount];
261
+ const tyreLabel = l?.replace("_", axlePosition.toString());
262
+ if (tyreLabel) return tyreLabel;
263
+ if (axleTyresCount < 2) return axlePosition.toString();
264
+ return tyrePosition.toString();
265
+ },
266
+ getPositionDescriptionInner(axlePosition, tyrePosition, axleTyresCount) {
267
+ const axle = `Axle ${axlePosition.toString()}`;
268
+ if (axleTyresCount === 4) {
269
+ const side = tyrePosition === 1 || tyrePosition === 2 ? "Left" : "Right";
270
+ const pos = tyrePosition === 1 || tyrePosition === axleTyresCount ? "Outer" : "Inner";
271
+ return `${axle}, ${side} ${pos}`;
272
+ }
273
+ if (axleTyresCount === 2) {
274
+ const side = tyrePosition === 1 ? "Left" : "Right";
275
+ return `${axle}, ${side}`;
276
+ }
277
+ if (axleTyresCount === 1) {
278
+ return `${axle}, ${"Center"}`;
279
+ }
280
+ return `${axle}, ${"Spare"}`;
281
+ },
282
+ getSpareTyres(tcVehicle) {
283
+ const spareTyres = tcVehicle.tcTyres?.filter((tyre) => String(tyre.mountedOn?.positionId).endsWith("0"));
284
+ return spareTyres;
112
285
  }
113
- // barToKpaByte(value?: number) {
114
- // if (!value) return 0
115
- // return _.round(((value + 1) * 100) / 5.0625)
116
- // },
117
- // kpaByteToBar(value?: number, decrementValue = 1) {
118
- // if (!_.isNumber(value)) {
119
- // throw new TypeError('Value has to be a number')
120
- // }
121
- // const rawBar = (value * 5.0625) / 100
122
- // return Math.max(rawBar - decrementValue, 0)
123
- // },
124
- // getMacAddressIfOtaAndIos(device: BluetoothDeviceBridge) {
125
- // const uint8 = new Uint8Array(device.advertising.kCBAdvDataLeBluetoothDeviceAddress)
126
- // const macAddressArray = Array.from(uint8)
127
- // .reverse()
128
- // .slice(0, 6)
129
- // .map(x => this.decimalToHex(x).toUpperCase())
130
- // return macAddressArray.join('')
131
- // },
132
- // isVersionGreaterThan(version: string) {
133
- // if (version?.length !== 5) {
134
- // throw new Error('Invalid version format'.t())
135
- // }
136
- // if (bluetoothStore?.connectedBridge?.advertisingData?.fwVersion) {
137
- // return bluetoothStore?.connectedBridge?.advertisingData?.fwVersion > version
138
- // }
139
- // return false
140
- // },
141
286
  };
287
+ function getDefaultTyreLabels() {
288
+ const tyreLabelsStr = "_L;_R;_LO;_LI;_RI;_RO;_";
289
+ const tyreLabelsStrLoc = tyreLabelsStr;
290
+ const tls = tyreLabelsStrLoc.split(";");
291
+ return {
292
+ 12: tls[0],
293
+ 22: tls[1],
294
+ 14: tls[2],
295
+ 24: tls[3],
296
+ 34: tls[4],
297
+ 44: tls[5],
298
+ 10: ___default.last(tls) || "",
299
+ 20: ___default.last(tls) || ""
300
+ };
301
+ }
142
302
 
143
303
  const bridgeAdvertisingParser = {
144
304
  getDeviceInfoFromAdvertising(device) {
145
305
  const adversitingType = getAdvertisingType(device);
146
- if (adversitingType !== "connectable")
147
- return void 0;
306
+ if (adversitingType !== "connectable") return void 0;
148
307
  const advertisingData = getAdvertisingData({ advertising: device.advertising, deviceName: device.name });
149
308
  const bridgeId = advertisingData?.macAddress?.map((n) => bridgeTools.decimalToHex(n)).reverse().join("").toUpperCase() || "";
150
309
  const vin = String.fromCharCode(...advertisingData?.vinNum || []).split("\0").join("");
151
- return { id: device.id, name: device.name, bridgeId, vin, advertisingData, device };
310
+ return { id: device.id, name: device.name, bridgeId, vin, advertisingData, type: "bridge", rssi: device.rssi };
152
311
  }
153
312
  };
154
313
  function getAdvertisingType(device) {
@@ -173,7 +332,7 @@ function getAdvertisingData({ advertising, deviceName }) {
173
332
  advertising.kCBAdvDataIsConnectable ? advertising.kCBAdvDataManufacturerData : advertising
174
333
  );
175
334
  const adv = Array.from(uint8);
176
- const advertisingData = advertising.kCBAdvDataIsConnectable ? processIosAdvertising(adv) : processAndroidAdvertising(adv, isKrone);
335
+ const advertisingData = advertising.kCBAdvDataIsConnectable ? processIosAdvertising(adv, isKrone) : processAndroidAdvertising(adv, isKrone);
177
336
  if (!advertisingData || !advertisingData.macAddress.length || !advertisingData.randomAdvNumber.length || !advertisingData.vinNum.length) {
178
337
  throw new Error("Device not recognized");
179
338
  }
@@ -213,145 +372,206 @@ function processAndroidAdvertising(adv, isKrone) {
213
372
  return advertisingData;
214
373
  }
215
374
  function processIosAdvertising(adv, isKrone) {
216
- if (adv.length < 34)
217
- return void 0;
375
+ const advertisingData = {
376
+ configVersion: isKrone ? 17 : 1,
377
+ macAddress: [],
378
+ randomAdvNumber: [],
379
+ vinNum: [],
380
+ fwVersion: void 0,
381
+ timeFromStart: void 0
382
+ };
383
+ console.log("adv.lenght", adv.length);
384
+ if (adv.length < 34) return void 0;
218
385
  if (adv.length === 35) {
219
- adv.slice(3, 11);
220
- adv.slice(12, 18);
221
- adv.slice(18, 35);
386
+ advertisingData.randomAdvNumber = adv.slice(3, 11);
387
+ advertisingData.macAddress = adv.slice(12, 18);
388
+ advertisingData.vinNum = adv.slice(18, 35);
222
389
  }
223
390
  if (adv.length === 40) {
224
- adv.slice(3, 11);
225
- bridgeTools.getFwVersion(adv.slice(11, 14));
226
- adv[14];
227
- adv[15];
228
- adv.slice(17, 23);
229
- adv.slice(23, 40);
391
+ advertisingData.randomAdvNumber = adv.slice(3, 11);
392
+ advertisingData.fwVersion = bridgeTools.getFwVersion(adv.slice(11, 14));
393
+ advertisingData.configVersion = adv[14];
394
+ advertisingData.timeFromStart = adv[15];
395
+ advertisingData.macAddress = adv.slice(17, 23);
396
+ advertisingData.vinNum = adv.slice(23, 40);
230
397
  }
398
+ return advertisingData;
231
399
  }
232
400
 
233
401
  const deviceMeta = {
234
402
  bridge: {
235
403
  nameRegex: /(030303|030321)/,
236
- characteristic: {
404
+ communication: {
237
405
  serviceId: "4880c12c-fdcb-4077-8920-a450d7f9b907",
238
406
  // message from device
239
407
  characteristicId: "fec26ec4-6d71-4442-9f81-55bc21d658d7"
240
408
  },
241
- capabilities: [],
242
- manufacturerId: 2978,
243
409
  mtu: 256,
244
410
  getDeviceInfoFromAdvertising: bridgeAdvertisingParser.getDeviceInfoFromAdvertising
245
411
  },
246
412
  bridgeOta: {
247
- nameRegex: /CAN BLE BRDG OTA.*/,
413
+ nameRegex: /(CAN BLE BRDG OTA.*|0303.{2}_B)/,
248
414
  characteristic: {
249
415
  serviceId: "4880c12c-fdcb-4077-8920-a450d7f9b907",
250
416
  characteristicId: "fec26ec4-6d71-4442-9f81-55bc21d658d7"
251
417
  },
252
- capabilities: [],
253
- getDeviceInfoFromAdvertising: () => {
418
+ getDeviceInfoFromAdvertising: (device) => {
419
+ const bleDevice = {
420
+ ...device,
421
+ type: "bridgeOta"
422
+ };
423
+ return bleDevice;
254
424
  }
255
425
  },
256
426
  flexiGaugeTpms: {
257
427
  nameRegex: /Flexi.*/,
258
- characteristic: {
428
+ communication: {
259
429
  serviceId: "4880c12c-fdcb-4077-8920-a450d7f9b907",
260
- // message from device
261
430
  characteristicId: "fec26ec4-6d71-4442-9f81-55bc21d658d6"
262
431
  },
263
- getDeviceInfoFromAdvertising: () => {
432
+ battery: {
433
+ serviceId: "180f",
434
+ characteristicId: "2a19"
435
+ },
436
+ getDeviceInfoFromAdvertising: (device) => {
437
+ const bleDevice = {
438
+ ...device,
439
+ type: "flexiGaugeTpms"
440
+ };
441
+ return bleDevice;
264
442
  },
265
- // Do we need it here?
266
- capabilities: [
267
- {
268
- id: "td",
269
- // processFn: processTreadDepth,
270
- regex: /^\*TD.*/
271
- },
272
- {
273
- id: "button",
274
- // processFn: processButtonPress,
275
- regex: /^\*[UDLRC] $/
276
- },
277
- {
278
- id: "tpms",
279
- // processFn: processTpms,
280
- regex: /^\*TPMS.*/
281
- }
282
- ],
283
443
  reconnect: true
284
444
  // Do we need it here?
285
- },
286
- pressureStick: {
287
- nameRegex: /Pressure Stick.*/,
288
- characteristic: {
289
- serviceId: "4880c12c-fdcb-4077-8920-a450d7f9b907",
290
- characteristicId: "fec26ec4-6d71-4442-9f81-55bc21d658d6"
291
- },
292
- getDeviceInfoFromAdvertising: () => {
293
- },
294
- capabilities: [
295
- {
296
- id: "pressure",
297
- // processFn: processPressure,
298
- regex: /P([0-9.]+)mBar/
299
- }
300
- // only pressure is needed for initial implementation, uncomment tpms functionality when needed
301
- // {
302
- // id: 'tpms',
303
- // processFn: processTpms,
304
- // regex: /^\*TPMS/,
305
- // },
306
- ]
307
445
  }
446
+ // pressureStick: {
447
+ // nameRegex: /Pressure Stick.*/,
448
+ // characteristic: {
449
+ // serviceId: '4880c12c-fdcb-4077-8920-a450d7f9b907',
450
+ // characteristicId: 'fec26ec4-6d71-4442-9f81-55bc21d658d6',
451
+ // },
452
+ // getDeviceInfoFromAdvertising: () => {},
453
+ // capabilities: [
454
+ // {
455
+ // id: 'pressure',
456
+ // // processFn: processPressure,
457
+ // regex: /P([0-9.]+)mBar/,
458
+ // },
459
+ // // only pressure is needed for initial implementation, uncomment tpms functionality when needed
460
+ // // {
461
+ // // id: 'tpms',
462
+ // // processFn: processTpms,
463
+ // // regex: /^\*TPMS/,
464
+ // // },
465
+ // ],
466
+ // },
308
467
  };
309
468
 
310
469
  const checkUnreachableDevicesTimeouts = {};
311
470
  const checkConnectedStateIntervals = {};
312
471
  let deviceAdvertisingCallback;
313
- let deviceUpdateCallback;
314
472
  let deviceUnreachableCallback;
473
+ let deviceLostConnectionCallback;
315
474
  const bluetooth = {
316
475
  /** Triggered when "scanDevices" detects device supported by SDK */
317
476
  onDeviceAdvertising(callback) {
318
477
  deviceAdvertisingCallback = callback;
319
478
  },
320
- onDeviceUpdate(callback) {
321
- deviceUpdateCallback = callback;
322
- },
323
479
  onDeviceUnreachable(callback) {
324
480
  deviceUnreachableCallback = callback;
325
481
  },
326
- async scanDevices(services = []) {
327
- await ble.stopScan();
328
- ble.startScanWithOptions(
329
- services,
330
- {
331
- reportDuplicates: true,
332
- matchMode: "aggressive",
333
- scanMode: "lowLatency",
334
- numOfMatches: "max",
335
- callbackType: "all",
336
- reportDelay: 0
337
- },
338
- (device) => {
339
- const processedDevice = processDevice(device);
340
- if (!processedDevice)
341
- return;
342
- deviceAdvertisingCallback?.(processedDevice);
343
- },
344
- (e) => console.error("ble.startScanWithOptions error:", e)
345
- );
346
- }
482
+ onDeviceLostConnection(callback) {
483
+ deviceLostConnectionCallback = callback;
484
+ },
485
+ scanDevices,
486
+ stopScan() {
487
+ ble.stopScan();
488
+ },
489
+ connect,
490
+ disconnect,
491
+ write,
492
+ read
347
493
  };
348
- function processDevice(device) {
349
- const uint8 = new Uint8Array(device.advertising);
350
- const adv = Array.from(uint8);
351
- let name = device.advertising.kCBAdvDataLocalName || device.name;
352
- if (!name || name === "OTA") {
353
- name = getDeviceNameFromAdvertising(adv);
494
+ async function scanDevices(services = [], duration) {
495
+ await ble.stopScan();
496
+ ble.startScanWithOptions(
497
+ services,
498
+ {
499
+ reportDuplicates: true,
500
+ matchMode: "aggressive",
501
+ scanMode: "lowLatency",
502
+ numOfMatches: "max",
503
+ callbackType: "all",
504
+ reportDelay: 0,
505
+ duration
506
+ },
507
+ (device) => {
508
+ const processedDevice = processDevice(device);
509
+ if (!processedDevice) return;
510
+ store.devices[processedDevice.id] = processedDevice;
511
+ deviceAdvertisingCallback?.(processedDevice);
512
+ },
513
+ (e) => console.error("ble.startScanWithOptions error:", e)
514
+ );
515
+ }
516
+ async function connect(deviceId, disconnectCallback) {
517
+ store.deviceState[deviceId] = "connecting";
518
+ let connectedDevice;
519
+ for (let attempt = 0; attempt < 3; attempt++) {
520
+ try {
521
+ connectedDevice = await connectInner(deviceId, disconnectCallback);
522
+ if (connectedDevice) break;
523
+ } catch (e) {
524
+ console.warn(`${attempt} connect fail`, e);
525
+ if (attempt === 3) throw new Error("Connection unsuccessful");
526
+ }
527
+ }
528
+ const isConnected = await ble.isConnected(deviceId);
529
+ if (!isConnected) {
530
+ store.deviceState[deviceId] = void 0;
531
+ throw new Error("Connect Error");
532
+ }
533
+ store.deviceState[deviceId] = "connected";
534
+ return connectedDevice;
535
+ }
536
+ function connectInner(deviceId, disconnectCallback) {
537
+ return new Promise(
538
+ (resolve, reject) => ble.connect(deviceId, resolve, (err) => {
539
+ if (toolsSvc.canCommunicateWith(deviceId)) {
540
+ deviceLostConnectionCallback?.(deviceId);
541
+ disconnectCallback(deviceId);
542
+ }
543
+ reject(err);
544
+ })
545
+ );
546
+ }
547
+ async function disconnect(deviceId) {
548
+ for (let attempts = 0; attempts < 3; attempts++) {
549
+ await withTimeout(ble.disconnect(deviceId), 1e3, "Disconnect timed out");
550
+ const isConnected = await ble.isConnected(deviceId);
551
+ if (!isConnected) return;
552
+ }
553
+ store.deviceState[deviceId] = void 0;
554
+ throw new Error("Disconnect unsuccessful");
555
+ }
556
+ async function write(deviceId, serviceUuid, characteristicUuid, value) {
557
+ if (!toolsSvc.canCommunicateWith(deviceId)) throw new Error("Write Error: Not connected to device");
558
+ try {
559
+ await ble.writeWithoutResponse(deviceId, serviceUuid, characteristicUuid, value);
560
+ } catch (e) {
561
+ throw new Error(`Write Error: ${e}`);
562
+ }
563
+ }
564
+ async function read(deviceId, serviceUuid, characteristicUuid) {
565
+ if (!toolsSvc.canCommunicateWith(deviceId)) throw new Error("Read Error: Not connected to device");
566
+ try {
567
+ const value = await ble.read(deviceId, serviceUuid, characteristicUuid);
568
+ return Array.from(new Uint8Array(value));
569
+ } catch (e) {
570
+ throw new Error(`Read Error: ${e}`);
354
571
  }
572
+ }
573
+ function processDevice(device) {
574
+ const name = getDeviceName(device);
355
575
  const deviceType = getDeviceTypeFromName(name);
356
576
  if (!deviceType) {
357
577
  return;
@@ -359,14 +579,12 @@ function processDevice(device) {
359
579
  let processedDevice;
360
580
  try {
361
581
  processedDevice = deviceMeta[deviceType].getDeviceInfoFromAdvertising({ ...device, name });
362
- if (!processedDevice)
363
- return;
582
+ if (!processedDevice) return;
364
583
  } catch (e) {
365
584
  console.warn("Error processing advertising", e);
366
585
  return;
367
586
  }
368
587
  refreshUnreachableTimeouts(processedDevice.id);
369
- monitorConnectedState(processedDevice.id);
370
588
  return processedDevice;
371
589
  }
372
590
  function getDeviceTypeFromName(name) {
@@ -379,20 +597,6 @@ function getDeviceTypeFromName(name) {
379
597
  }
380
598
  return deviceType;
381
599
  }
382
- function monitorConnectedState(deviceId) {
383
- if (checkConnectedStateIntervals[deviceId])
384
- return;
385
- checkConnectedStateIntervals[deviceId] = setInterval(async () => {
386
- let isConnected = false;
387
- try {
388
- await ble.isConnected(deviceId);
389
- isConnected = true;
390
- } catch {
391
- isConnected = false;
392
- }
393
- deviceUpdateCallback?.({ deviceId, isConnected });
394
- }, 1e3);
395
- }
396
600
  function refreshUnreachableTimeouts(deviceId) {
397
601
  if (checkUnreachableDevicesTimeouts[deviceId]) {
398
602
  clearTimeout(checkUnreachableDevicesTimeouts[deviceId]);
@@ -405,21 +609,2354 @@ function refreshUnreachableTimeouts(deviceId) {
405
609
  }
406
610
  }, 6e4);
407
611
  }
408
- function getDeviceNameFromAdvertising(adv) {
409
- const advertising = ___default.clone(adv);
410
- const messageType = advertising[4];
411
- const packet = advertising.slice(5, 11);
412
- if (messageType === 9) {
413
- return packet.map((x) => String.fromCharCode(x)).join("");
612
+ function getDeviceName(device) {
613
+ const isBridge = deviceMeta.bridge.nameRegex.test(device.name) || deviceMeta.bridgeOta.nameRegex.test(device.name);
614
+ if (isBridge && !device.advertising.kCBAdvDataLocalName) {
615
+ return getDeviceNameFromAdvertising(device.advertising);
414
616
  }
415
- return "";
617
+ return device.advertising.kCBAdvDataLocalName ?? device.name;
618
+ }
619
+ function getDeviceNameFromAdvertising(advertising) {
620
+ const adv = new Uint8Array(advertising);
621
+ const length = adv[3];
622
+ const type = adv[4];
623
+ if (type !== 9) {
624
+ console.warn("Bridge name not found in advertising");
625
+ return "";
626
+ }
627
+ const decoder = new TextDecoder("utf-8");
628
+ const deviceName = decoder.decode(adv.slice(5, 4 + length));
629
+ return deviceName;
630
+ }
631
+
632
+ const bridgeCommandStructures = {
633
+ bridgeConfiguration: {
634
+ id: [98, 1],
635
+ name: "bridgeConfiguration",
636
+ structure: {
637
+ configVersion: {
638
+ size: 4,
639
+ description: "Application configuration version details"
640
+ },
641
+ bleTxPower: {
642
+ size: 2,
643
+ description: "Used BLE Tx power (16 bit signed value; LSB = 0.1 LSB)",
644
+ display: "decimal"
645
+ },
646
+ bleTxChannels: {
647
+ size: 1,
648
+ description: "BLE channel configuration (0x07 = all channels)"
649
+ },
650
+ bleConnectableAdvPeriod: {
651
+ size: 2,
652
+ description: "Ble Connectable Advertisement Period [LSB = 0.625 ms]",
653
+ display: "decimal"
654
+ },
655
+ configurationFlags: {
656
+ size: 1,
657
+ description: "Configuration flags (1 = Enable inactivity timeout; 4 = Disable time lockout on false PINs)"
658
+ },
659
+ wrongPinUntilLockout: {
660
+ size: 1,
661
+ display: "decimal",
662
+ description: "Minimimal number of wrong pins allowed before the Lockout will be activated for a period"
663
+ },
664
+ maxLockoutPeriodIncreases: {
665
+ display: "decimal",
666
+ size: 1,
667
+ description: "How many times it is allowed to double the lockout period"
668
+ },
669
+ wrongPinInitialLockoutTime: {
670
+ size: 1,
671
+ display: "decimal",
672
+ description: "What is the initial lockout time duration after too many wrong PINs"
673
+ },
674
+ securityConfiguration: {
675
+ size: 1,
676
+ description: "Bluetooth security configuration (0x03 - legacy pairing enabled; 0x07 - legacy pairing disabled)"
677
+ },
678
+ canEnabled: {
679
+ size: 1,
680
+ display: "decimal",
681
+ description: "CAN enabled state"
682
+ },
683
+ bleEnabled: {
684
+ size: 1,
685
+ display: "decimal",
686
+ description: "BLE Advertisement enabled state"
687
+ },
688
+ canBitrate: {
689
+ size: 2,
690
+ display: "decimal",
691
+ description: "CAN Bitrate [kbps]"
692
+ },
693
+ movementDetectionLimit: {
694
+ size: 2,
695
+ display: "decimal",
696
+ description: "Movement detection limit [1/256 kmph per bit]"
697
+ },
698
+ sensorInactivityTimeout: {
699
+ size: 2,
700
+ display: "decimal",
701
+ description: "Timeout [s] counter after which inactive sensor is marked as erroneous (incremented when vehicle is moving; max. 512 seconds)"
702
+ },
703
+ manufacturerCode: {
704
+ size: 4,
705
+ display: "decimal",
706
+ description: "Manufacturer Code as per SAE J1939"
707
+ },
708
+ identityNumber: {
709
+ size: 4,
710
+ display: "decimal",
711
+ description: "Identity number for ECU (Max. 22 bits value = 0x1FFFFF)"
712
+ },
713
+ manufacturerName: {
714
+ size: 20,
715
+ description: "Manufacturer Name (Max. 20 bytes length)",
716
+ display: "ascii"
717
+ },
718
+ ecuPartNumber: {
719
+ size: 20,
720
+ description: "ECU Part Number (Max. 20 bytes length)",
721
+ display: "ascii"
722
+ },
723
+ softwareVersion: {
724
+ size: 20,
725
+ description: "Software Version (Max. 20 bytes length)",
726
+ display: "ascii"
727
+ },
728
+ reservedConfiguration: {
729
+ size: 4,
730
+ display: "decimal",
731
+ description: "Reserved configuration"
732
+ }
733
+ }
734
+ },
735
+ workshopCanSettings: {
736
+ id: [98, 96],
737
+ name: "workshopCanSettings",
738
+ structure: {
739
+ canTermination: {
740
+ size: 1,
741
+ description: "CAN Termination disabled / enabled"
742
+ }
743
+ }
744
+ },
745
+ customerCanSettings: {
746
+ id: [98, 80],
747
+ name: "customerCanSettings",
748
+ structure: {
749
+ canProtocol: {
750
+ size: 1,
751
+ description: "J1939 / ECE-R141 CAN Protocol Status"
752
+ },
753
+ transparentFilteredMode: {
754
+ size: 1,
755
+ description: "0th bit = CAN Transparent/Filtered Mode; 1st bit = BLE Transparent/Filtered Mode"
756
+ },
757
+ spnsPgnsBitmap: {
758
+ size: 4,
759
+ description: "SPNs/PGNs Bitmap Toggled State"
760
+ }
761
+ }
762
+ },
763
+ axleImbalanceThresholds: {
764
+ id: [98, 66],
765
+ name: "axleImbalanceThresholds",
766
+ structure: {
767
+ axle01: {
768
+ size: 1,
769
+ description: "Axle 01 imbalance limits (encoded pressure and temperature imbalance limits)"
770
+ },
771
+ axle02: {
772
+ size: 1,
773
+ description: "Axle 02 imbalance limits (encoded pressure and temperature imbalance limits)"
774
+ },
775
+ axle03: {
776
+ size: 1,
777
+ description: "Axle 03 imbalance limits (encoded pressure and temperature imbalance limits)"
778
+ },
779
+ axle04: {
780
+ size: 1,
781
+ description: "Axle 04 imbalance limits (encoded pressure and temperature imbalance limits)"
782
+ },
783
+ axle05: {
784
+ size: 1,
785
+ description: "Axle 05 imbalance limits (encoded pressure and temperature imbalance limits)"
786
+ },
787
+ axle06: {
788
+ size: 1,
789
+ description: "Axle 06 imbalance limits (encoded pressure and temperature imbalance limits)"
790
+ },
791
+ axle07: {
792
+ size: 1,
793
+ description: "Axle 07 imbalance limits (encoded pressure and temperature imbalance limits)"
794
+ },
795
+ axle08: {
796
+ size: 1,
797
+ description: "Axle 08 imbalance limits (encoded pressure and temperature imbalance limits)"
798
+ },
799
+ axle09: {
800
+ size: 1,
801
+ description: "Axle 09 imbalance limits (encoded pressure and temperature imbalance limits)"
802
+ },
803
+ axle10: {
804
+ size: 1,
805
+ description: "Axle 10 imbalance limits (encoded pressure and temperature imbalance limits)"
806
+ },
807
+ axle11: {
808
+ size: 1,
809
+ description: "Axle 11 imbalance limits (encoded pressure and temperature imbalance limits)"
810
+ },
811
+ axle12: {
812
+ size: 1,
813
+ description: "Axle 12 imbalance limits (encoded pressure and temperature imbalance limits)"
814
+ },
815
+ axle13: {
816
+ size: 1,
817
+ description: "Axle 13 imbalance limits (encoded pressure and temperature imbalance limits)"
818
+ },
819
+ axle14: {
820
+ size: 1,
821
+ description: "Axle 14 imbalance limits (encoded pressure and temperature imbalance limits)"
822
+ },
823
+ axle15: {
824
+ size: 1,
825
+ description: "Axle 15 imbalance limits (encoded pressure and temperature imbalance limits)"
826
+ }
827
+ }
828
+ },
829
+ axleTemperatureThresholds: {
830
+ id: [98, 65],
831
+ name: "axleTemperatureThresholds",
832
+ structure: {
833
+ axle01: {
834
+ size: 1,
835
+ description: "Axle 01 over-temperature limit"
836
+ },
837
+ axle02: {
838
+ size: 1,
839
+ description: "Axle 02 over-temperature limit"
840
+ },
841
+ axle03: {
842
+ size: 1,
843
+ description: "Axle 03 over-temperature limit"
844
+ },
845
+ axle04: {
846
+ size: 1,
847
+ description: "Axle 04 over-temperature limit"
848
+ },
849
+ axle05: {
850
+ size: 1,
851
+ description: "Axle 05 over-temperature limit"
852
+ },
853
+ axle06: {
854
+ size: 1,
855
+ description: "Axle 06 over-temperature limit"
856
+ },
857
+ axle07: {
858
+ size: 1,
859
+ description: "Axle 07 over-temperature limit"
860
+ },
861
+ axle08: {
862
+ size: 1,
863
+ description: "Axle 08 over-temperature limit"
864
+ },
865
+ axle09: {
866
+ size: 1,
867
+ description: "Axle 09 over-temperature limit"
868
+ },
869
+ axle10: {
870
+ size: 1,
871
+ description: "Axle 10 over-temperature limit"
872
+ },
873
+ axle11: {
874
+ size: 1,
875
+ description: "Axle 11 over-temperature limit"
876
+ },
877
+ axle12: {
878
+ size: 1,
879
+ description: "Axle 12 over-temperature limit"
880
+ },
881
+ axle13: {
882
+ size: 1,
883
+ description: "Axle 13 over-temperature limit"
884
+ },
885
+ axle14: {
886
+ size: 1,
887
+ description: "Axle 14 over-temperature limit"
888
+ },
889
+ axle15: {
890
+ size: 1,
891
+ description: "Axle 15 over-temperature limit"
892
+ }
893
+ }
894
+ },
895
+ axlePressureThresholds: {
896
+ id: [98, 64],
897
+ name: "axlePressureThresholds",
898
+ structure: {
899
+ axle01: {
900
+ size: 4,
901
+ description: "Axle 01 pressure delta limits (encoded extreme over-; over-; under-; extreme under-pressure limit)"
902
+ },
903
+ axle02: {
904
+ size: 4,
905
+ description: "Axle 02 pressure delta limits (encoded extreme over-; over-; under-; extreme under-pressure limit)"
906
+ },
907
+ axle03: {
908
+ size: 4,
909
+ description: "Axle 03 pressure delta limits (encoded extreme over-; over-; under-; extreme under-pressure limit)"
910
+ },
911
+ axle04: {
912
+ size: 4,
913
+ description: "Axle 04 pressure delta limits (encoded extreme over-; over-; under-; extreme under-pressure limit)"
914
+ },
915
+ axle05: {
916
+ size: 4,
917
+ description: "Axle 05 pressure delta limits (encoded extreme over-; over-; under-; extreme under-pressure limit)"
918
+ },
919
+ axle06: {
920
+ size: 4,
921
+ description: "Axle 06 pressure delta limits (encoded extreme over-; over-; under-; extreme under-pressure limit)"
922
+ },
923
+ axle07: {
924
+ size: 4,
925
+ description: "Axle 07 pressure delta limits (encoded extreme over-; over-; under-; extreme under-pressure limit)"
926
+ },
927
+ axle08: {
928
+ size: 4,
929
+ description: "Axle 08 pressure delta limits (encoded extreme over-; over-; under-; extreme under-pressure limit)"
930
+ },
931
+ axle09: {
932
+ size: 4,
933
+ description: "Axle 09 pressure delta limits (encoded extreme over-; over-; under-; extreme under-pressure limit)"
934
+ },
935
+ axle10: {
936
+ size: 4,
937
+ description: "Axle 10 pressure delta limits (encoded extreme over-; over-; under-; extreme under-pressure limit)"
938
+ },
939
+ axle11: {
940
+ size: 4,
941
+ description: "Axle 11 pressure delta limits (encoded extreme over-; over-; under-; extreme under-pressure limit)"
942
+ },
943
+ axle12: {
944
+ size: 4,
945
+ description: "Axle 12 pressure delta limits (encoded extreme over-; over-; under-; extreme under-pressure limit)"
946
+ },
947
+ axle13: {
948
+ size: 4,
949
+ description: "Axle 13 pressure delta limits (encoded extreme over-; over-; under-; extreme under-pressure limit)"
950
+ },
951
+ axle14: {
952
+ size: 4,
953
+ description: "Axle 14 pressure delta limits (encoded extreme over-; over-; under-; extreme under-pressure limit)"
954
+ },
955
+ axle15: {
956
+ size: 4,
957
+ description: "Axle 15 pressure delta limits (encoded extreme over-; over-; under-; extreme under-pressure limit)"
958
+ }
959
+ }
960
+ },
961
+ pressuresPerAxle: {
962
+ id: [98, 48],
963
+ lsb: true,
964
+ name: "pressuresPerAxle",
965
+ structure: {
966
+ axle01: {
967
+ size: 3,
968
+ description: "Axle 01 set pressure; min pressure; max pressure"
969
+ },
970
+ axle02: {
971
+ size: 3,
972
+ description: "Axle 02 set pressure; min pressure; max pressure"
973
+ },
974
+ axle03: {
975
+ size: 3,
976
+ description: "Axle 03 set pressure; min pressure; max pressure"
977
+ },
978
+ axle04: {
979
+ size: 3,
980
+ description: "Axle 04 set pressure; min pressure; max pressure"
981
+ },
982
+ axle05: {
983
+ size: 3,
984
+ description: "Axle 05 set pressure; min pressure; max pressure"
985
+ },
986
+ axle06: {
987
+ size: 3,
988
+ description: "Axle 06 set pressure; min pressure; max pressure"
989
+ },
990
+ axle07: {
991
+ size: 3,
992
+ description: "Axle 07 set pressure; min pressure; max pressure"
993
+ },
994
+ axle08: {
995
+ size: 3,
996
+ description: "Axle 08 set pressure; min pressure; max pressure"
997
+ },
998
+ axle09: {
999
+ size: 3,
1000
+ description: "Axle 09 set pressure; min pressure; max pressure"
1001
+ },
1002
+ axle10: {
1003
+ size: 3,
1004
+ description: "Axle 10 set pressure; min pressure; max pressure"
1005
+ },
1006
+ axle11: {
1007
+ size: 3,
1008
+ description: "Axle 11 set pressure; min pressure; max pressure"
1009
+ },
1010
+ axle12: {
1011
+ size: 3,
1012
+ description: "Axle 12 set pressure; min pressure; max pressure"
1013
+ },
1014
+ axle13: {
1015
+ size: 3,
1016
+ description: "Axle 13 set pressure; min pressure; max pressure"
1017
+ },
1018
+ axle14: {
1019
+ size: 3,
1020
+ description: "Axle 14 set pressure; min pressure; max pressure"
1021
+ },
1022
+ axle15: {
1023
+ size: 3,
1024
+ description: "Axle 15 set pressure; min pressure; max pressure"
1025
+ }
1026
+ }
1027
+ },
1028
+ autolearnSettings: {
1029
+ id: [98, 160],
1030
+ name: "autolearnSettings",
1031
+ structure: {
1032
+ axle01: {
1033
+ size: 1,
1034
+ description: "Axle 01 type, steer, liftable..."
1035
+ },
1036
+ axle02: {
1037
+ size: 1,
1038
+ description: "Axle 02 type, steer, liftable..."
1039
+ },
1040
+ axle03: {
1041
+ size: 1,
1042
+ description: "Axle 03 type, steer, liftable..."
1043
+ },
1044
+ axle04: {
1045
+ size: 1,
1046
+ description: "Axle 04 type, steer, liftable..."
1047
+ },
1048
+ axle05: {
1049
+ size: 1,
1050
+ description: "Axle 05 type, steer, liftable..."
1051
+ },
1052
+ axle06: {
1053
+ size: 1,
1054
+ description: "Axle 06 type, steer, liftable..."
1055
+ },
1056
+ axle07: {
1057
+ size: 1,
1058
+ description: "Axle 07 type, steer, liftable..."
1059
+ },
1060
+ axle08: {
1061
+ size: 1,
1062
+ description: "Axle 08 type, steer, liftable..."
1063
+ },
1064
+ axle09: {
1065
+ size: 1,
1066
+ description: "Axle 09 type, steer, liftable..."
1067
+ },
1068
+ axle10: {
1069
+ size: 1,
1070
+ description: "Axle 10 type, steer, liftable..."
1071
+ },
1072
+ axle11: {
1073
+ size: 1,
1074
+ description: "Axle 11 type, steer, liftable..."
1075
+ },
1076
+ axle12: {
1077
+ size: 1,
1078
+ description: "Axle 12 type, steer, liftable..."
1079
+ },
1080
+ axle13: {
1081
+ size: 1,
1082
+ description: "Axle 13 type, steer, liftable..."
1083
+ },
1084
+ axle14: {
1085
+ size: 1,
1086
+ description: "Axle 14 type, steer, liftable..."
1087
+ },
1088
+ axle15: {
1089
+ size: 1,
1090
+ description: "Axle 15 type, steer, liftable..."
1091
+ },
1092
+ isAutolearnEnabled: {
1093
+ size: 1,
1094
+ description: "Is autolearn enabled: 0 - disabled, 1 - enabled"
1095
+ },
1096
+ checkingPeriod: {
1097
+ size: 1,
1098
+ description: "Autolearn is evaluated within this period (when enabled)"
1099
+ },
1100
+ multiIdTimeout: {
1101
+ size: 1,
1102
+ description: "This timer (2 sec per bit) detects if multiple tyres / sensor IDs were changed and therefore autolearn cannot identify the tyre position of the new sensors"
1103
+ },
1104
+ temperatureLimit: {
1105
+ size: 1,
1106
+ description: "Autolearn temperature difference acceptation limit"
1107
+ }
1108
+ }
1109
+ },
1110
+ autolearnIdStatus: {
1111
+ id: [98, 176],
1112
+ name: "autolearnIdStatus",
1113
+ structure: {
1114
+ axle01: {
1115
+ size: 8,
1116
+ description: "Axle 01 autolearn statuses, each nibble signifies a single wheel."
1117
+ },
1118
+ axle02: {
1119
+ size: 8,
1120
+ description: "Axle 02 autolearn statuses, each nibble signifies a single wheel"
1121
+ },
1122
+ axle03: {
1123
+ size: 8,
1124
+ description: "Axle 03 autolearn statuses, each nibble signifies a single wheel"
1125
+ },
1126
+ axle04: {
1127
+ size: 8,
1128
+ description: "Axle 04 autolearn statuses, each nibble signifies a single wheel"
1129
+ },
1130
+ axle05: {
1131
+ size: 8,
1132
+ description: "Axle 05 autolearn statuses, each nibble signifies a single wheel"
1133
+ },
1134
+ axle06: {
1135
+ size: 8,
1136
+ description: "Axle 06 autolearn statuses, each nibble signifies a single wheel"
1137
+ },
1138
+ axle07: {
1139
+ size: 8,
1140
+ description: "Axle 07 autolearn statuses, each nibble signifies a single wheel"
1141
+ },
1142
+ axle08: {
1143
+ size: 8,
1144
+ description: "Axle 08 autolearn statuses, each nibble signifies a single wheel"
1145
+ },
1146
+ axle09: {
1147
+ size: 8,
1148
+ description: "Axle 09 autolearn statuses, each nibble signifies a single wheel"
1149
+ },
1150
+ axle10: {
1151
+ size: 8,
1152
+ description: "Axle 10 autolearn statuses, each nibble signifies a single wheel"
1153
+ },
1154
+ axle11: {
1155
+ size: 8,
1156
+ description: "Axle 11 autolearn statuses, each nibble signifies a single wheel"
1157
+ },
1158
+ axle12: {
1159
+ size: 8,
1160
+ description: "Axle 12 autolearn statuses, each nibble signifies a single wheel"
1161
+ },
1162
+ axle13: {
1163
+ size: 8,
1164
+ description: "Axle 13 autolearn statuses, each nibble signifies a single wheel"
1165
+ },
1166
+ axle14: {
1167
+ size: 8,
1168
+ description: "Axle 14 autolearn statuses, each nibble signifies a single wheel"
1169
+ },
1170
+ axle15: {
1171
+ size: 8,
1172
+ description: "Axle 15 autolearn statuses, each nibble signifies a single wheel"
1173
+ }
1174
+ }
1175
+ },
1176
+ autolearnUnknownSensors: {
1177
+ id: [98, 192],
1178
+ name: "autolearnUnknownSensors",
1179
+ structure: {
1180
+ sensor01: {
1181
+ size: 8,
1182
+ description: "Unknown sensor data"
1183
+ },
1184
+ sensor02: {
1185
+ size: 8,
1186
+ description: "Unknown sensor data"
1187
+ },
1188
+ sensor03: {
1189
+ size: 8,
1190
+ description: "Unknown sensor data"
1191
+ },
1192
+ sensor04: {
1193
+ size: 8,
1194
+ description: "Unknown sensor data"
1195
+ },
1196
+ sensor05: {
1197
+ size: 8,
1198
+ description: "Unknown sensor data"
1199
+ },
1200
+ sensor06: {
1201
+ size: 8,
1202
+ description: "Unknown sensor data"
1203
+ },
1204
+ sensor07: {
1205
+ size: 8,
1206
+ description: "Unknown sensor data"
1207
+ },
1208
+ sensor08: {
1209
+ size: 8,
1210
+ description: "Unknown sensor data"
1211
+ },
1212
+ sensor09: {
1213
+ size: 8,
1214
+ description: "Unknown sensor data"
1215
+ },
1216
+ sensor10: {
1217
+ size: 8,
1218
+ description: "Unknown sensor data"
1219
+ },
1220
+ sensor11: {
1221
+ size: 8,
1222
+ description: "Unknown sensor data"
1223
+ },
1224
+ sensor12: {
1225
+ size: 8,
1226
+ description: "Unknown sensor data"
1227
+ },
1228
+ sensor13: {
1229
+ size: 8,
1230
+ description: "Unknown sensor data"
1231
+ },
1232
+ sensor14: {
1233
+ size: 8,
1234
+ description: "Unknown sensor data"
1235
+ },
1236
+ sensor15: {
1237
+ size: 8,
1238
+ description: "Unknown sensor data"
1239
+ }
1240
+ }
1241
+ }
1242
+ };
1243
+
1244
+ const signature_keys = {
1245
+ 0: [
1246
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
1247
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 31],
1248
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 47],
1249
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 63],
1250
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 79],
1251
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 95],
1252
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 111],
1253
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 127],
1254
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 143],
1255
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 159],
1256
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 175],
1257
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 191],
1258
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 207],
1259
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 223],
1260
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 239],
1261
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 255]
1262
+ ],
1263
+ 1: [
1264
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
1265
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 31],
1266
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 47],
1267
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 63],
1268
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 79],
1269
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 95],
1270
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 111],
1271
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 127],
1272
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 143],
1273
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 159],
1274
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 175],
1275
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 191],
1276
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 207],
1277
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 223],
1278
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 239],
1279
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 255]
1280
+ ],
1281
+ 2: [
1282
+ [121, 114, 34, 71, 3, 133, 92, 108, 162, 173, 161, 168, 164, 148, 34, 139],
1283
+ [136, 120, 146, 44, 228, 28, 205, 63, 205, 120, 170, 120, 251, 245, 227, 154],
1284
+ [37, 22, 60, 132, 154, 66, 59, 185, 36, 142, 163, 103, 39, 121, 227, 209],
1285
+ [15, 30, 54, 173, 21, 127, 65, 158, 168, 84, 192, 17, 51, 3, 90, 173],
1286
+ [136, 165, 72, 127, 72, 38, 200, 5, 4, 187, 203, 29, 6, 82, 130, 39],
1287
+ [106, 95, 111, 82, 191, 213, 104, 194, 106, 30, 199, 250, 69, 223, 79, 250],
1288
+ [150, 63, 177, 61, 50, 182, 227, 53, 157, 168, 245, 96, 90, 212, 17, 132],
1289
+ [233, 159, 182, 205, 3, 216, 203, 73, 193, 142, 183, 16, 22, 208, 38, 143],
1290
+ [7, 45, 239, 102, 47, 8, 237, 115, 77, 232, 2, 167, 47, 65, 164, 251],
1291
+ [7, 159, 18, 71, 3, 132, 84, 47, 60, 167, 134, 251, 114, 207, 254, 190],
1292
+ [203, 129, 34, 153, 32, 169, 153, 45, 215, 172, 76, 214, 5, 21, 210, 65],
1293
+ [29, 194, 255, 4, 195, 144, 178, 174, 255, 66, 15, 19, 26, 212, 61, 251],
1294
+ [42, 13, 136, 252, 143, 234, 120, 242, 164, 36, 21, 135, 252, 145, 101, 124],
1295
+ [24, 24, 207, 236, 159, 24, 180, 141, 80, 119, 86, 239, 192, 32, 51, 34],
1296
+ [187, 89, 217, 2, 16, 125, 121, 46, 159, 66, 85, 169, 9, 155, 2, 178],
1297
+ [212, 230, 71, 216, 35, 20, 241, 0, 204, 163, 80, 198, 132, 210, 166, 173]
1298
+ ],
1299
+ 16: [
1300
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
1301
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 31],
1302
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 47],
1303
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 63],
1304
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 79],
1305
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 95],
1306
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 111],
1307
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 127],
1308
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 143],
1309
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 159],
1310
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 175],
1311
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 191],
1312
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 207],
1313
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 223],
1314
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 239],
1315
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 255]
1316
+ ],
1317
+ 17: [
1318
+ [75, 40, 61, 20, 254, 102, 105, 160, 44, 236, 79, 39, 84, 27, 235, 100],
1319
+ [185, 139, 202, 38, 158, 109, 211, 6, 38, 73, 28, 120, 208, 168, 159, 108],
1320
+ [252, 100, 192, 127, 135, 127, 228, 125, 190, 210, 248, 146, 45, 229, 218, 159],
1321
+ [217, 39, 58, 200, 22, 212, 105, 55, 15, 45, 82, 96, 216, 134, 69, 123],
1322
+ [192, 34, 199, 199, 200, 112, 61, 98, 174, 62, 7, 200, 149, 168, 175, 110],
1323
+ [60, 41, 125, 116, 134, 191, 241, 188, 171, 127, 209, 10, 114, 52, 120, 164],
1324
+ [255, 19, 220, 184, 141, 224, 242, 117, 230, 143, 20, 75, 44, 50, 70, 113],
1325
+ [55, 112, 171, 176, 239, 50, 111, 3, 175, 145, 123, 38, 12, 44, 18, 79],
1326
+ [211, 119, 219, 6, 27, 41, 170, 199, 206, 66, 89, 89, 154, 128, 110, 70],
1327
+ [187, 184, 42, 154, 108, 182, 238, 223, 192, 10, 144, 139, 130, 22, 220, 66],
1328
+ [91, 248, 195, 246, 212, 104, 246, 252, 231, 120, 153, 211, 39, 250, 221, 133],
1329
+ [184, 148, 72, 207, 106, 143, 21, 239, 133, 206, 1, 234, 1, 75, 92, 43],
1330
+ [46, 35, 117, 127, 103, 37, 155, 16, 177, 106, 195, 129, 168, 235, 159, 162],
1331
+ [0, 7, 114, 77, 77, 66, 224, 223, 92, 193, 241, 44, 242, 104, 67, 163],
1332
+ [116, 194, 13, 179, 75, 181, 245, 211, 19, 38, 45, 21, 36, 163, 252, 235],
1333
+ [6, 211, 31, 66, 81, 221, 137, 33, 23, 18, 160, 147, 159, 34, 121, 125]
1334
+ ]
1335
+ };
1336
+ const pinKeys = {
1337
+ 0: "00000000000000010000000000000002",
1338
+ 1: "00000000000000010000000000000002",
1339
+ 2: "B2D24304FC345F1418EB96835B3B7AE8",
1340
+ 16: "00000000000000010000000000000002",
1341
+ 17: "CB5A6AA23FD565A141C5CE5ABF209000"
1342
+ };
1343
+ const bridgeSecurity = {
1344
+ getSignedCommand(device, command) {
1345
+ const { randomAdvNumber, configVersion } = device?.advertisingData || {};
1346
+ if (!randomAdvNumber || !___default.isNumber(configVersion)) return console.error("random not present", randomAdvNumber);
1347
+ const rand = ___default.clone(randomAdvNumber);
1348
+ const security_level = bridgeTools.decimalToHex(command[0]);
1349
+ const keyIndex = Number(security_level[0]);
1350
+ const keyShift = Number(security_level[1]);
1351
+ let paddedCommand = [];
1352
+ if (command.length <= 16) {
1353
+ paddedCommand = bridgeTools.pkcs(command, 16);
1354
+ } else {
1355
+ let t = Math.floor(command.length / 16);
1356
+ const r = command.length % 16;
1357
+ paddedCommand = bridgeTools.pkcs(command, r ? ++t * 16 : t * 16);
1358
+ }
1359
+ const key = signature_keys[configVersion][keyIndex];
1360
+ const startArray = key.slice(keyShift);
1361
+ const endArray = key.slice(0, keyShift);
1362
+ const shiftedKey = startArray.concat(endArray).map((n) => bridgeTools.decimalToHex(n)).join("");
1363
+ const dataIn = [...rand.reverse(), ...Array.from({ length: 8 }).fill(8)].map((x) => bridgeTools.decimalToHex(x)).join("");
1364
+ const keyWA = CryptoJs__default.enc.Hex.parse(shiftedKey);
1365
+ const dataInWA = CryptoJs__default.enc.Hex.parse(dataIn);
1366
+ const encrypted = CryptoJs__default.AES.encrypt(dataInWA, keyWA, {
1367
+ mode: CryptoJs__default.mode.ECB,
1368
+ // ECB mode is used here for simplicity. Consider more secure modes.
1369
+ padding: CryptoJs__default.pad.Pkcs7
1370
+ // We handle padding manually
1371
+ });
1372
+ const iv = CryptoJs__default.enc.Hex.stringify(encrypted.ciphertext).slice(0, 32);
1373
+ const nBlocks = paddedCommand.length / 16 + 1;
1374
+ let blockVector = this.encryptedStringToNum(iv);
1375
+ for (let index = 1; index < nBlocks; index++) {
1376
+ const block = paddedCommand.slice((index - 1) * 16, index * 16);
1377
+ const xorBlock = [];
1378
+ for (let index2 = 0; index2 < block.length; index2++) {
1379
+ xorBlock.push(block[index2] ^ blockVector[index2]);
1380
+ }
1381
+ const vector = blockVector.map((x) => bridgeTools.decimalToHex(x)).join("");
1382
+ const dataWa = CryptoJs__default.enc.Hex.parse(xorBlock.map((x) => bridgeTools.decimalToHex(x)).join(""));
1383
+ const testo = CryptoJs__default.AES.encrypt(dataWa, keyWA, {
1384
+ iv: CryptoJs__default.enc.Hex.parse(vector),
1385
+ mode: CryptoJs__default.mode.ECB,
1386
+ padding: CryptoJs__default.pad.Pkcs7
1387
+ });
1388
+ const result = CryptoJs__default.enc.Hex.stringify(testo.ciphertext);
1389
+ blockVector = this.encryptedStringToNum(result.slice(0, 32));
1390
+ }
1391
+ const signature = blockVector.slice(0, 4).reverse();
1392
+ return [...command, ...signature];
1393
+ },
1394
+ async getPin(deviceId) {
1395
+ const deviceData = store.devices[deviceId];
1396
+ if (!deviceData) throw new Error("Device data missing");
1397
+ const { randomAdvNumber, macAddress, configVersion } = deviceData.advertisingData || {};
1398
+ if (!randomAdvNumber || !macAddress || !___default.isNumber(configVersion)) throw new Error("Cannot compute pin");
1399
+ const mac = macAddress.map((n) => bridgeTools.decimalToHex(n)).join("");
1400
+ const rand = randomAdvNumber.map((n) => bridgeTools.decimalToHex(n)).join("");
1401
+ const message = `${mac}0000${rand}`;
1402
+ const key = pinKeys[configVersion];
1403
+ const res = await this.aesToCrc32(message, key);
1404
+ return res;
1405
+ },
1406
+ aesToCrc32(message, key) {
1407
+ const messageHex = CryptoJs__default.enc.Hex.parse(message);
1408
+ const keyHex = CryptoJs__default.enc.Hex.parse(key);
1409
+ const encrypted = CryptoJs__default.AES.encrypt(messageHex, keyHex, {
1410
+ mode: CryptoJs__default.mode.ECB,
1411
+ // You can use other modes like CBC or CTR for additional security
1412
+ padding: CryptoJs__default.pad.ZeroPadding
1413
+ // You can use other padding modes as well
1414
+ });
1415
+ const encryptedByteString = CryptoJs__default.enc.Hex.stringify(encrypted.ciphertext).slice(0, 32);
1416
+ const hexArray = this.encryptedStringToNum(encryptedByteString);
1417
+ return hexArray;
1418
+ },
1419
+ encryptedStringToNum(encString) {
1420
+ const hexArray = [];
1421
+ for (let i = 0; i < encString.length; i += 2) {
1422
+ const slicedString = encString.slice(i, i + 2);
1423
+ hexArray.push(Number(`0x${slicedString}`));
1424
+ }
1425
+ return hexArray;
1426
+ },
1427
+ crc32mpeg2(msg) {
1428
+ let crc = 4294967295;
1429
+ for (let i = 0; i < msg.length; i++) {
1430
+ const b = msg[i];
1431
+ crc ^= b << 24;
1432
+ for (let j = 0; j < 8; j++) {
1433
+ crc = crc << 1 ^ (crc & 2147483648 ? 79764919 : 0);
1434
+ }
1435
+ }
1436
+ return crc >>> 0;
1437
+ },
1438
+ getCrcArray(msg) {
1439
+ const crc = this.crc32mpeg2(msg);
1440
+ const crcHex = bridgeTools.decimalToHex(crc).padStart(8, "0");
1441
+ return bridgeTools.hexToDecimalArray(crcHex).reverse();
1442
+ },
1443
+ uint32ToLittleEndian(value) {
1444
+ const byteArray = new Uint8Array(4);
1445
+ byteArray[0] = value & 255;
1446
+ byteArray[1] = value >> 8 & 255;
1447
+ byteArray[2] = value >> 16 & 255;
1448
+ byteArray[3] = value >> 24 & 255;
1449
+ return byteArray;
1450
+ }
1451
+ };
1452
+
1453
+ const bridgeMeta = deviceMeta.bridge;
1454
+ let promiseQueue = Promise.resolve();
1455
+ let currentResolve = (_v) => {
1456
+ };
1457
+ let currentReject = (_v) => {
1458
+ };
1459
+ let responseIdentifier;
1460
+ const promiseQueue$1 = {
1461
+ clearQueue(message) {
1462
+ currentReject(new Error(message ?? "Stopped sending commands"));
1463
+ promiseQueue = Promise.resolve();
1464
+ responseIdentifier = [];
1465
+ },
1466
+ async enqueue(device, payload) {
1467
+ promiseQueue = promiseQueue.then(() => {
1468
+ const promise = new Promise((resolve, reject) => {
1469
+ currentResolve = resolve;
1470
+ currentReject = reject;
1471
+ const isKeepAlive = payload[3] === commandIds.keepAlive;
1472
+ responseIdentifier = [isKeepAlive ? 126 : 0, payload[4]];
1473
+ const signedData = bridgeSecurity.getSignedCommand(device, payload);
1474
+ if (!toolsSvc.canCommunicateWith(device.id)) return reject(new Error("Bridge not connected"));
1475
+ if (signedData.length > 110) {
1476
+ const chunks = [];
1477
+ chunks.push(signedData.slice(0, 110));
1478
+ chunks.push(signedData.slice(110));
1479
+ for (const chunk of chunks) {
1480
+ const convertedChunk = new Uint8Array(chunk).buffer;
1481
+ bluetooth.write(
1482
+ device.id,
1483
+ bridgeMeta.communication.serviceId,
1484
+ bridgeMeta.communication.characteristicId,
1485
+ convertedChunk
1486
+ );
1487
+ }
1488
+ } else {
1489
+ const convertedData = new Uint8Array(signedData).buffer;
1490
+ bluetooth.write(
1491
+ device.id,
1492
+ bridgeMeta.communication.serviceId,
1493
+ bridgeMeta.communication.characteristicId,
1494
+ convertedData
1495
+ );
1496
+ }
1497
+ });
1498
+ return withTimeout(promise, 5e3, "Command timed out");
1499
+ });
1500
+ return promiseQueue;
1501
+ },
1502
+ async processMessage(deviceId, payload) {
1503
+ const numberArray = Array.from(new Uint8Array(payload));
1504
+ if (numberArray[3] === 127) {
1505
+ return currentReject(new Error(`Command not succesful: ${numberArray[4]}`));
1506
+ }
1507
+ if ([226, 81].includes(numberArray[3]) && numberArray[4] && toolsSvc.canCommunicateWith(deviceId)) {
1508
+ store.bridgesReboot[deviceId] = true;
1509
+ }
1510
+ if ((!responseIdentifier[0] || numberArray[3] === responseIdentifier[0]) && (!responseIdentifier[1] || numberArray[4] === responseIdentifier[1])) {
1511
+ return currentResolve(payload);
1512
+ }
1513
+ console.warn("message from the device not belonging to the pending promise: ", payload);
1514
+ }
1515
+ };
1516
+
1517
+ const securityLevelIds = {
1518
+ tirecheck: 1,
1519
+ vehicleManufacturer: 16,
1520
+ vehicleDealership: 32,
1521
+ workshop: 48,
1522
+ driver: 64
1523
+ };
1524
+ const messageTypeIds = {
1525
+ command: 0,
1526
+ response: 1
1527
+ };
1528
+ const commandIds = {
1529
+ readData: 34,
1530
+ writeData: 46,
1531
+ keepAlive: 62,
1532
+ keepAliveResponse: 126,
1533
+ sensorMeasurement: 189,
1534
+ sensorMeasurementResponse: 253,
1535
+ ecuReset: 17,
1536
+ reboot: 162,
1537
+ pin: 18,
1538
+ pinResponse: 90,
1539
+ otaRequest: 167
1540
+ };
1541
+ const subCommandIds = {
1542
+ ecuResetSubCommand: 0,
1543
+ rebootSubCommand: 4,
1544
+ bridgeConfiguration: 1,
1545
+ vehicleLayout: 16,
1546
+ /** 0x20 - 0x2F */
1547
+ idsPerWheel: (axleIndex) => 32 + axleIndex,
1548
+ /** only for commandIds.sensorMeasurement */
1549
+ sensorMeasurementPerWheel: (axlePosition, tyrePosition, isTwinTyre, isSpareAxle) => {
1550
+ let bridgeTyrePosition = 0;
1551
+ if (isTwinTyre && !isSpareAxle) {
1552
+ if (tyrePosition === 1) bridgeTyrePosition = 6;
1553
+ else if (tyrePosition === 2) bridgeTyrePosition = 7;
1554
+ else if (tyrePosition === 3) bridgeTyrePosition = 9;
1555
+ else if (tyrePosition === 4) bridgeTyrePosition = 10;
1556
+ } else if (!isTwinTyre && !isSpareAxle) {
1557
+ if (tyrePosition === 1) bridgeTyrePosition = 7;
1558
+ else if (tyrePosition === 2) bridgeTyrePosition = 9;
1559
+ } else if (isSpareAxle) {
1560
+ if (tyrePosition === 1) bridgeTyrePosition = 8;
1561
+ }
1562
+ return axlePosition * 16 + bridgeTyrePosition;
1563
+ },
1564
+ pressurePerAxle: 48,
1565
+ customerPressureThresholds: 64,
1566
+ customerTemperatureThresholds: 65,
1567
+ customerImbalanceThresholds: 66,
1568
+ customerCanSettings: 80,
1569
+ workshopCanSettings: 96,
1570
+ errorHistoryCounter: 112,
1571
+ eolStatus: 128,
1572
+ firmwareVersion: 144,
1573
+ autolearnSettings: 160,
1574
+ autolearnIdStatus: 176,
1575
+ autolearnUnknownSensors: 192
1576
+ };
1577
+ ___default.invert(commandIds);
1578
+ ___default.invert(subCommandIds);
1579
+ let keepAliveTimer;
1580
+ const bridgeCommands = {
1581
+ getCommandHeader(device) {
1582
+ return [securityLevelIds.tirecheck, messageTypeIds.command];
1583
+ },
1584
+ async setAxlesPressure(deviceId, data) {
1585
+ const deviceData = getDeviceData(deviceId);
1586
+ await this.writeCommand(deviceData, commandIds.writeData, subCommandIds.pressurePerAxle, data);
1587
+ },
1588
+ async setVehicleLayout(deviceId, data) {
1589
+ const deviceData = getDeviceData(deviceId);
1590
+ await this.writeCommand(deviceData, commandIds.writeData, subCommandIds.vehicleLayout, data);
1591
+ },
1592
+ async getCustomerCANSettings(deviceId) {
1593
+ const deviceData = getDeviceData(deviceId);
1594
+ const result = await this.readCommand(deviceData, subCommandIds.customerCanSettings);
1595
+ return bridgeTools.convertBytesToStructure(bridgeCommandStructures.customerCanSettings.structure, result);
1596
+ },
1597
+ async getWorkshopCANSettings(deviceId) {
1598
+ const deviceData = getDeviceData(deviceId);
1599
+ const result = await this.readCommand(deviceData, subCommandIds.workshopCanSettings);
1600
+ return bridgeTools.convertBytesToStructure(bridgeCommandStructures.workshopCanSettings.structure, result);
1601
+ },
1602
+ async getBridgeConfiguration(deviceId) {
1603
+ const deviceData = getDeviceData(deviceId);
1604
+ const result = await this.readCommand(deviceData, subCommandIds.bridgeConfiguration);
1605
+ return bridgeTools.convertBytesToStructure(bridgeCommandStructures.bridgeConfiguration.structure, result);
1606
+ },
1607
+ async getCustomerPressureThresholds(deviceId) {
1608
+ const deviceData = getDeviceData(deviceId);
1609
+ const result = await this.readCommand(deviceData, subCommandIds.customerPressureThresholds);
1610
+ return bridgeTools.convertBytesToStructure(bridgeCommandStructures.axlePressureThresholds.structure, result);
1611
+ },
1612
+ async getCustomerTemperatureThresholds(deviceId) {
1613
+ const deviceData = getDeviceData(deviceId);
1614
+ const result = await this.readCommand(deviceData, subCommandIds.customerTemperatureThresholds);
1615
+ return bridgeTools.convertBytesToStructure(bridgeCommandStructures.axleTemperatureThresholds.structure, result);
1616
+ },
1617
+ async getCustomerImbalanceThresholds(deviceId) {
1618
+ const deviceData = getDeviceData(deviceId);
1619
+ const result = await this.readCommand(deviceData, subCommandIds.customerImbalanceThresholds);
1620
+ return bridgeTools.convertBytesToStructure(bridgeCommandStructures.axleImbalanceThresholds.structure, result);
1621
+ },
1622
+ async sendKeepAliveCommand(device) {
1623
+ return this.promisify(device, [...this.getCommandHeader(device), 2, commandIds.keepAlive, 0]);
1624
+ },
1625
+ async sendOtaRequest(deviceId) {
1626
+ const deviceData = getDeviceData(deviceId);
1627
+ const command = [commandIds.otaRequest, 0];
1628
+ const result = await this.promisify(deviceData, [...this.getCommandHeader(deviceData), command.length, ...command]);
1629
+ return result;
1630
+ },
1631
+ async sendPinCommand(deviceId) {
1632
+ const deviceData = await getDeviceData(deviceId);
1633
+ const pin = await bridgeSecurity.getPin(deviceId);
1634
+ return this.promisify(deviceData, [...this.getCommandHeader(deviceData), 18, commandIds.pin, 0, ...pin]);
1635
+ },
1636
+ async setBridgeToRestart(deviceId) {
1637
+ const deviceData = await getDeviceData(deviceId);
1638
+ const useNewCommand = (deviceData.advertisingData.fwVersion || "") > "0.9.7";
1639
+ await this.promisify(deviceData, [
1640
+ ...this.getCommandHeader(deviceData),
1641
+ 2,
1642
+ useNewCommand ? commandIds.ecuReset : commandIds.reboot,
1643
+ useNewCommand ? subCommandIds.ecuResetSubCommand : subCommandIds.rebootSubCommand
1644
+ ]);
1645
+ },
1646
+ async setCustomerCANSettings(deviceId, structurizedPayload) {
1647
+ const deviceData = getDeviceData(deviceId);
1648
+ const payload = bridgeTools.convertStructureToBytes(
1649
+ bridgeCommandStructures.customerCanSettings.structure,
1650
+ structurizedPayload
1651
+ );
1652
+ return await this.writeCommand(deviceData, commandIds.writeData, subCommandIds.customerCanSettings, payload);
1653
+ },
1654
+ async getCustomerCrcSettings(deviceId) {
1655
+ const result = await this.readCrcCommand(deviceId, subCommandIds.customerCanSettings);
1656
+ let factorySettings = false;
1657
+ if (!result?.length || result.every((n) => n === 0)) factorySettings = true;
1658
+ return factorySettings;
1659
+ },
1660
+ async readCrcCommand(deviceId, subCommandId) {
1661
+ const deviceData = getDeviceData(deviceId);
1662
+ const commandLength = 2;
1663
+ const result = await this.promisify(deviceData, [
1664
+ ...this.getCommandHeader(deviceData),
1665
+ commandLength,
1666
+ commandIds.readData,
1667
+ subCommandId
1668
+ ]);
1669
+ const crc = result.slice(result.length - 8, result.length - 4);
1670
+ return crc;
1671
+ },
1672
+ async setWorkshopCANSettings(deviceId, structurizedPayload) {
1673
+ const deviceData = getDeviceData(deviceId);
1674
+ const payload = bridgeTools.convertStructureToBytes(
1675
+ bridgeCommandStructures.workshopCanSettings.structure,
1676
+ structurizedPayload
1677
+ );
1678
+ return await this.writeCommand(deviceData, commandIds.writeData, subCommandIds.workshopCanSettings, payload);
1679
+ },
1680
+ async setBridgeConfiguration(deviceId, structurizedPayload) {
1681
+ const deviceData = getDeviceData(deviceId);
1682
+ const payload = bridgeTools.convertStructureToBytes(
1683
+ bridgeCommandStructures.bridgeConfiguration.structure,
1684
+ structurizedPayload
1685
+ );
1686
+ return await this.writeCommand(deviceData, commandIds.writeData, subCommandIds.bridgeConfiguration, payload);
1687
+ },
1688
+ async setCustomerPressureThresholds(deviceId, structurizedPayload) {
1689
+ const deviceData = getDeviceData(deviceId);
1690
+ const payload = bridgeTools.convertStructureToBytes(
1691
+ bridgeCommandStructures.axlePressureThresholds.structure,
1692
+ structurizedPayload
1693
+ );
1694
+ return await this.writeCommand(deviceData, commandIds.writeData, subCommandIds.customerPressureThresholds, payload);
1695
+ },
1696
+ async setCustomerTemperatureThresholds(deviceId, structurizedPayload) {
1697
+ const deviceData = getDeviceData(deviceId);
1698
+ const payload = bridgeTools.convertStructureToBytes(
1699
+ bridgeCommandStructures.axleTemperatureThresholds.structure,
1700
+ structurizedPayload
1701
+ );
1702
+ return await this.writeCommand(
1703
+ deviceData,
1704
+ commandIds.writeData,
1705
+ subCommandIds.customerTemperatureThresholds,
1706
+ payload
1707
+ );
1708
+ },
1709
+ async setCustomerImbalanceThresholds(deviceId, structurizedPayload) {
1710
+ const deviceData = getDeviceData(deviceId);
1711
+ const payload = bridgeTools.convertStructureToBytes(
1712
+ bridgeCommandStructures.axleImbalanceThresholds.structure,
1713
+ structurizedPayload
1714
+ );
1715
+ return await this.writeCommand(deviceData, commandIds.writeData, subCommandIds.customerImbalanceThresholds, payload);
1716
+ },
1717
+ async getAutolearnSettings(deviceId) {
1718
+ const deviceData = getDeviceData(deviceId);
1719
+ const result = await this.readCommand(deviceData, subCommandIds.autolearnSettings);
1720
+ return bridgeTools.convertBytesToStructure(bridgeCommandStructures.autolearnSettings.structure, result);
1721
+ },
1722
+ async getAutolearnIdStatus(deviceId) {
1723
+ const deviceData = getDeviceData(deviceId);
1724
+ const result = await this.readCommand(deviceData, subCommandIds.autolearnIdStatus);
1725
+ return bridgeTools.convertBytesToStructure(bridgeCommandStructures.autolearnIdStatus.structure, result);
1726
+ },
1727
+ async setAutoLearnSettings(deviceId, structurizedPayload) {
1728
+ const deviceData = getDeviceData(deviceId);
1729
+ const payload = bridgeTools.convertStructureToBytes(
1730
+ bridgeCommandStructures.autolearnSettings.structure,
1731
+ structurizedPayload
1732
+ );
1733
+ return await this.writeCommand(deviceData, commandIds.writeData, subCommandIds.autolearnSettings, payload);
1734
+ },
1735
+ async setAutolearnIdStatus(deviceId, structurizedPayload) {
1736
+ const deviceData = getDeviceData(deviceId);
1737
+ const payload = bridgeTools.convertStructureToBytes(
1738
+ bridgeCommandStructures.autolearnIdStatus.structure,
1739
+ structurizedPayload
1740
+ );
1741
+ return await this.writeCommand(deviceData, commandIds.writeData, subCommandIds.autolearnIdStatus, payload);
1742
+ },
1743
+ async getAutolearnUnknownSensors(device) {
1744
+ const result = await this.readCommand(device, subCommandIds.autolearnUnknownSensors);
1745
+ return bridgeTools.convertBytesToStructure(bridgeCommandStructures.autolearnUnknownSensors.structure, result);
1746
+ },
1747
+ async getAxleInfo(deviceId, axleIndex) {
1748
+ const deviceData = getDeviceData(deviceId);
1749
+ if (!___default.inRange(axleIndex, 0, 16)) throw new Error("Error getting an axle");
1750
+ const result = await this.readCommand(deviceData, subCommandIds.idsPerWheel(axleIndex));
1751
+ return result;
1752
+ },
1753
+ // move logic to bridge svc
1754
+ async setAxleInfo(deviceId, axleIndex, data) {
1755
+ const deviceData = getDeviceData(deviceId);
1756
+ await this.writeCommand(deviceData, commandIds.writeData, subCommandIds.idsPerWheel(axleIndex), data);
1757
+ },
1758
+ async getAxlesPressure(deviceId) {
1759
+ const deviceData = getDeviceData(deviceId);
1760
+ const result = await this.readCommand(deviceData, subCommandIds.pressurePerAxle);
1761
+ return result;
1762
+ },
1763
+ async getPressuresPerAxle(deviceId) {
1764
+ const deviceData = getDeviceData(deviceId);
1765
+ const result = await this.readCommand(deviceData, subCommandIds.pressurePerAxle);
1766
+ const structurizedData = bridgeTools.convertBytesToStructure(
1767
+ bridgeCommandStructures.pressuresPerAxle.structure,
1768
+ result
1769
+ );
1770
+ return structurizedData;
1771
+ },
1772
+ async setPressuresPerAxle(deviceId, structurizedPayload) {
1773
+ const deviceData = getDeviceData(deviceId);
1774
+ const payload = bridgeTools.convertStructureToBytes(
1775
+ bridgeCommandStructures.pressuresPerAxle.structure,
1776
+ structurizedPayload
1777
+ );
1778
+ return await this.writeCommand(deviceData, commandIds.writeData, subCommandIds.pressurePerAxle, payload);
1779
+ },
1780
+ // // async setPressureThresholds(device: BleBridge, rules: any) {
1781
+ // // // https://tirecheck.atlassian.net/wiki/spaces/HWPRG/pages/6547767302/Technical+Design+Document+BLE+CAN+Bridge+Krone#Example.1
1782
+ // // let numberArray = [
1783
+ // // rules?.underinflation?.valueCritical || null,
1784
+ // // rules?.underinflation?.valueWarning || null,
1785
+ // // rules?.overinflation?.valueWarning || null,
1786
+ // // rules?.overinflation?.valueCritical || null,
1787
+ // // ]
1788
+ // // if (numberArray.includes(null)) {
1789
+ // // const defaultRules = userStore.maybeTcAccount?.ruleset || { underinflation: {}, overinflation: {} }
1790
+ // // const defaultArray = [
1791
+ // // defaultRules?.underinflation?.valueCritical || 100,
1792
+ // // defaultRules?.underinflation?.valueWarning || 90,
1793
+ // // defaultRules?.overinflation?.valueWarning || 90,
1794
+ // // defaultRules?.overinflation?.valueCritical || 100, // default values when threshold is not set should be discussed with managenemt
1795
+ // // ]
1796
+ // // numberArray = numberArray.map((x, index) => (x === null ? defaultArray[index] : x))
1797
+ // // }
1798
+ // // const allAxles = Array(15).fill(numberArray).flat()
1799
+ // // await this.writeCommand(device, commandIds.writeData, subCommandIds.customerPressureThresholds, allAxles)
1800
+ // // },
1801
+ async getVehicleLayout(deviceId) {
1802
+ const deviceData = getDeviceData(deviceId);
1803
+ const result = await this.readCommand(deviceData, subCommandIds.vehicleLayout);
1804
+ return result;
1805
+ },
1806
+ async getSensorMeasurement(deviceId, positionId) {
1807
+ const deviceData = getDeviceData(deviceId);
1808
+ const isSpare = String(positionId).endsWith("0");
1809
+ const positionInfo = bridgeTools.getPositionInfo(positionId);
1810
+ const { axlePosition, tyrePosition, isTwinTyre } = positionInfo;
1811
+ if (axlePosition == null || isTwinTyre == null || tyrePosition == null) {
1812
+ throw new Error(`Invalid positionId ${positionId}`);
1813
+ }
1814
+ const result = await this.writeCommand(
1815
+ deviceData,
1816
+ commandIds.sensorMeasurement,
1817
+ subCommandIds.sensorMeasurementPerWheel(axlePosition, tyrePosition, isTwinTyre, isSpare),
1818
+ []
1819
+ );
1820
+ return result;
1821
+ },
1822
+ async readCommand(device, subCommandId) {
1823
+ const commandLength = 2;
1824
+ const result = await this.promisify(device, [
1825
+ ...this.getCommandHeader(device),
1826
+ commandLength,
1827
+ commandIds.readData,
1828
+ subCommandId
1829
+ ]);
1830
+ const data = result.slice(19, result.length - 8);
1831
+ return data;
1832
+ },
1833
+ async writeCommand(device, commandId, subCommandId, payload) {
1834
+ const commandHead = this.getCommandHeader(device);
1835
+ const macAddress = [0, 0, 0, 0, 0, 0];
1836
+ const timestamp = bridgeTools.decimalToHex(Math.floor(Date.now() / 1e3));
1837
+ const parsedTimestamp = timestamp.match(/.{2}/g)?.map((x) => Number.parseInt(x, 16));
1838
+ if (parsedTimestamp?.length !== 4) throw new Error("Wrong timestamp format");
1839
+ const paddedTimestamp = [...parsedTimestamp, 0, 0, 0, 0];
1840
+ const crc = bridgeSecurity.getCrcArray([...macAddress, ...paddedTimestamp, ...payload]);
1841
+ if (crc.length !== 4) throw new Error("Wrong crc length");
1842
+ const commandLength = macAddress.length + paddedTimestamp.length + crc.length + payload.length + 2;
1843
+ const writeCommand = [
1844
+ ...commandHead,
1845
+ commandLength,
1846
+ commandId,
1847
+ subCommandId,
1848
+ ...macAddress,
1849
+ ...paddedTimestamp,
1850
+ ...payload,
1851
+ ...crc
1852
+ ];
1853
+ if ([subCommandIds.vehicleLayout, subCommandIds.customerCanSettings, subCommandIds.workshopCanSettings].includes(
1854
+ subCommandId
1855
+ )) {
1856
+ await this.setBridgeToRestart(device.id);
1857
+ }
1858
+ return await this.promisify(device, writeCommand);
1859
+ },
1860
+ async promisify(device, writeCommand) {
1861
+ if (!writeCommand.every((n) => ___default.isNumber(n))) {
1862
+ console.error("invallid command", writeCommand);
1863
+ throw new Error("Invalid command");
1864
+ }
1865
+ if (!canCommunicateWith(device.id)) throw new Error("Bridge not connected");
1866
+ clearTimeout(keepAliveTimer);
1867
+ keepAliveTimer = setTimeout(() => {
1868
+ if (!canCommunicateWith(device.id)) {
1869
+ return;
1870
+ }
1871
+ this.sendKeepAliveCommand(device);
1872
+ }, 1e4);
1873
+ return promiseQueue$1.enqueue(device, writeCommand);
1874
+ }
1875
+ };
1876
+
1877
+ const otaServiceUuid = "1d14d6ee-fd63-4fa1-bfa4-8f47b42119f0";
1878
+ const otaControlCharacteristicUuid = "f7bf3564-fb6d-4e53-88a4-5e37e0326063";
1879
+ const otaDataCharacteristicUuid = "984227f3-34fc-4045-a5d0-2c581f81a153";
1880
+ const bridgeOtaCommands = {
1881
+ async beginOta(deviceId) {
1882
+ await bluetooth.write(deviceId, otaServiceUuid, otaControlCharacteristicUuid, new Uint8Array([0]).buffer);
1883
+ },
1884
+ async uploadOtaChunk(deviceId, data) {
1885
+ await bluetooth.write(deviceId, otaServiceUuid, otaDataCharacteristicUuid, data);
1886
+ },
1887
+ async endOta(deviceId) {
1888
+ await bluetooth.write(deviceId, otaServiceUuid, otaControlCharacteristicUuid, new Uint8Array([3]).buffer);
1889
+ }
1890
+ };
1891
+
1892
+ const mtu = 180;
1893
+ const bridgeOtaService = {
1894
+ async updateFirmware(deviceId, bootloader, firmware, progressCallback) {
1895
+ await delay(2e3);
1896
+ progressCallback("Connecting to the bridge...", 0.1);
1897
+ await bridgeOta.connect(deviceId);
1898
+ progressCallback("Uploading bootloader...", 0.12);
1899
+ await bridgeOtaCommands.beginOta(deviceId);
1900
+ await uploadOta(deviceId, bootloader, (str, percents) => progressCallback(str, 0.12 + percents * 0.2));
1901
+ await bridgeOtaCommands.endOta(deviceId);
1902
+ progressCallback("Uploading application...", 0.32);
1903
+ await bridgeOtaCommands.beginOta(deviceId);
1904
+ await uploadOta(deviceId, firmware, (str, percents) => progressCallback(str, 0.32 + percents * 0.5));
1905
+ await bridgeOtaCommands.endOta(deviceId);
1906
+ progressCallback("Upload completed, disconnecting...", 0.81);
1907
+ await bridgeOta.disconnect(deviceId);
1908
+ progressCallback("Disconnected...", 0.82);
1909
+ }
1910
+ };
1911
+ async function uploadOta(deviceId, firmwareBinary, reportStatus) {
1912
+ let uploadedBytes = 0;
1913
+ let chunkIndex = 0;
1914
+ while (uploadedBytes < firmwareBinary.byteLength) {
1915
+ if (chunkIndex % 100 === 0)
1916
+ reportStatus(
1917
+ `Uploading new firmware... (${___default.round(uploadedBytes / 1e3)} / ${___default.round(
1918
+ firmwareBinary.byteLength / 1e3
1919
+ )} KB)`,
1920
+ uploadedBytes / firmwareBinary.byteLength
1921
+ );
1922
+ const chunkSize = Math.min(mtu - 3, firmwareBinary.byteLength - uploadedBytes);
1923
+ const chunk = firmwareBinary.slice(uploadedBytes, uploadedBytes + chunkSize);
1924
+ await bridgeOtaCommands.uploadOtaChunk(deviceId, chunk);
1925
+ uploadedBytes += chunkSize;
1926
+ chunkIndex++;
1927
+ }
1928
+ }
1929
+
1930
+ const bridgeOta = {
1931
+ async connect(deviceId) {
1932
+ await bluetooth.connect(deviceId, this.disconnect);
1933
+ store.deviceState[deviceId] = "paired";
1934
+ },
1935
+ async disconnect(deviceId) {
1936
+ store.deviceState[deviceId] = "disconnecting";
1937
+ await bluetooth.disconnect(deviceId);
1938
+ store.deviceState[deviceId] = "disconnected";
1939
+ },
1940
+ async updateFirmware(deviceId, bootloader, firmware, progressCallback) {
1941
+ return bridgeOtaService.updateFirmware(deviceId, bootloader, firmware, progressCallback);
1942
+ }
1943
+ };
1944
+
1945
+ const vehicleLayoutAxleTypes = {
1946
+ noAxle: [0, 0],
1947
+ twoTyresAxle: [128, 2],
1948
+ twoTyresAxleSpare: [128, 3],
1949
+ fourTyresAxle: [192, 6],
1950
+ fourTyresAxleSpare: [192, 7],
1951
+ spareTyreAxle: [1, 0]
1952
+ };
1953
+ const bridgeService = {
1954
+ updateFirmware,
1955
+ getVehicle,
1956
+ setVehicle,
1957
+ getConfiguration,
1958
+ setConfiguration,
1959
+ getSensorReading,
1960
+ getVehicleReadings,
1961
+ resetAutolearnStatuses,
1962
+ getAutolearnStatuses
1963
+ };
1964
+ async function updateFirmware(deviceId, bootloader, firmware, reportStatus) {
1965
+ reportStatus("Sending OTA Request...", 0.02);
1966
+ await bridgeCommands.sendOtaRequest(deviceId);
1967
+ await toolsSvc.delay(2e3);
1968
+ await bluetooth.scanDevices();
1969
+ reportStatus("Discovering OTA Device...", 0.02);
1970
+ await discoverOtaBridge(deviceId, (str, percents) => reportStatus(str, 0.02 + percents * 0.1));
1971
+ await bridgeOta.updateFirmware(deviceId, bootloader, firmware, reportStatus);
1972
+ reportStatus("Update completed", 1);
1973
+ await waitForBridgeToReconnect(deviceId, (str, percents) => reportStatus(str, 0.85 + percents * 0.15));
1974
+ reportStatus("Update finished!", 1);
1975
+ }
1976
+ async function setVehicleLayout(deviceId, tcVehicle) {
1977
+ const spareTyres = tcVehicle.tcTyres?.filter((tyre) => String(tyre.mountedOn?.positionId).endsWith("0"));
1978
+ let spareTyresCount = Math.min(spareTyres.length || 0, 2);
1979
+ let layout = [];
1980
+ for (let index = 0; index < 15; index++) {
1981
+ const tyresCount = tcVehicle.axles && tcVehicle.axles[index]?.tyresCount;
1982
+ if (tyresCount === 2) {
1983
+ let value = vehicleLayoutAxleTypes.twoTyresAxle;
1984
+ if (spareTyresCount) {
1985
+ value = vehicleLayoutAxleTypes.twoTyresAxleSpare;
1986
+ spareTyresCount -= 1;
1987
+ }
1988
+ layout = [...layout, ...value];
1989
+ continue;
1990
+ }
1991
+ if (tyresCount === 4) {
1992
+ let value = vehicleLayoutAxleTypes.fourTyresAxle;
1993
+ if (spareTyresCount) {
1994
+ value = vehicleLayoutAxleTypes.fourTyresAxleSpare;
1995
+ spareTyresCount -= 1;
1996
+ }
1997
+ layout = [...layout, ...value];
1998
+ continue;
1999
+ }
2000
+ layout = [...layout, ...vehicleLayoutAxleTypes.noAxle];
2001
+ }
2002
+ let vin = tcVehicle.vin ? tcVehicle.vin.split("").map((x) => x.charCodeAt(0)) : [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
2003
+ if (vin.length !== 17) {
2004
+ throw new Error(`Incorrect VIN length: ${vin}`);
2005
+ }
2006
+ if (isVersionGreaterThan(deviceId, "1.0.2")) {
2007
+ vin = [...vin, 0];
2008
+ }
2009
+ await bridgeCommands.setVehicleLayout(deviceId, [...layout, ...vin]);
2010
+ }
2011
+ async function getConfiguration(deviceId) {
2012
+ const customerCANSettings = await bridgeCommands.getCustomerCANSettings(deviceId);
2013
+ const workshopCANSettings = await bridgeCommands.getWorkshopCANSettings(deviceId);
2014
+ const bridgeConfiguration = await bridgeCommands.getBridgeConfiguration(deviceId);
2015
+ const customerPressureThresholds = await bridgeCommands.getCustomerPressureThresholds(deviceId);
2016
+ const customerTemperatureThresholds = await bridgeCommands.getCustomerTemperatureThresholds(deviceId);
2017
+ const customerImbalanceThresholds = await bridgeCommands.getCustomerImbalanceThresholds(deviceId);
2018
+ const pressuresPerAxle = await bridgeCommands.getPressuresPerAxle(deviceId);
2019
+ let autolearnSettings;
2020
+ if (isVersionGreaterThan(deviceId, "0.9.7")) {
2021
+ autolearnSettings = await bridgeCommands.getAutolearnSettings(deviceId);
2022
+ }
2023
+ return {
2024
+ customerCANSettings,
2025
+ workshopCANSettings,
2026
+ bridgeConfiguration,
2027
+ customerPressureThresholds,
2028
+ customerTemperatureThresholds,
2029
+ customerImbalanceThresholds,
2030
+ pressuresPerAxle,
2031
+ autolearnSettings
2032
+ };
2033
+ }
2034
+ function bridgeVehiclesDifference(original, change) {
2035
+ const differences = [];
2036
+ if (original.tcBridge?.id !== change.tcBridge?.id) differences.push("bridgeId");
2037
+ if (original.vin !== change.vin) differences.push("vin");
2038
+ const originalTyres = original.tcTyres.map((t) => {
2039
+ return { positionId: t.mountedOn?.positionId, sensorId: t.tcTpmsSensor?.id || null };
2040
+ });
2041
+ const changeTyres = change.tcTyres?.map((t) => {
2042
+ return { positionId: t.mountedOn?.positionId, sensorId: t.tcTpmsSensor?.id || null };
2043
+ });
2044
+ if (!___default.isEqual(___default.sortBy(changeTyres, "positionId"), ___default.sortBy(originalTyres, "positionId")))
2045
+ differences.push("sensorPosition");
2046
+ const originalAxlesPressure = original.axles.map((a) => {
2047
+ return {
2048
+ targetPressure: a.targetPressure,
2049
+ maxTargetPressure: a.maxTargetPressure,
2050
+ minTargetPressure: a.minTargetPressure
2051
+ };
2052
+ });
2053
+ const changeAxlesPressure = change.axles.map((a) => {
2054
+ return {
2055
+ targetPressure: a.targetPressure,
2056
+ maxTargetPressure: a.maxTargetPressure,
2057
+ minTargetPressure: a.minTargetPressure
2058
+ };
2059
+ });
2060
+ if (!___default.isEqual(changeAxlesPressure, originalAxlesPressure)) differences.push("axlePressure");
2061
+ const originalAxleType = original.axles.map((a) => {
2062
+ return {
2063
+ isSteer: a.isSteer,
2064
+ isSpare: a.isSpare,
2065
+ isLift: a.isLift
2066
+ };
2067
+ });
2068
+ const changeAxleType = change.axles.map((a) => {
2069
+ return {
2070
+ isSteer: a.isSteer,
2071
+ isSpare: a.isSpare,
2072
+ isLift: a.isLift
2073
+ };
2074
+ });
2075
+ if (!___default.isEqual(changeAxleType, originalAxleType)) differences.push("axleType");
2076
+ const originalTyresCount = original.axles.map((a) => a.tyresCount);
2077
+ const changeTyresCount = change.axles.map((a) => a.tyresCount);
2078
+ if (!___default.isEqual(originalTyresCount, changeTyresCount)) differences.push("vehicleLayout");
2079
+ return { isDifferent: !!differences.length, differences };
2080
+ }
2081
+ async function bridgeConfigurationDifference(original, change) {
2082
+ const propertiesToCheck = [
2083
+ "customerCANSettings",
2084
+ "workshopCANSettings",
2085
+ "customerPressureThresholds",
2086
+ "customerTemperatureThresholds",
2087
+ "customerImbalanceThresholds",
2088
+ "pressuresPerAxle"
2089
+ ];
2090
+ const differentProps = [];
2091
+ for (const prop of propertiesToCheck) {
2092
+ for (const key in change[prop]) {
2093
+ if (original[prop][key] !== change[prop][key]) {
2094
+ differentProps.push(prop);
2095
+ break;
2096
+ }
2097
+ }
2098
+ }
2099
+ return { isDifferent: !!differentProps.length, differences: differentProps };
2100
+ }
2101
+ function getVehicleAxlesTypes(tcVehicle) {
2102
+ const filteredAxles = tcVehicle?.axles?.filter((axle) => !axle.isSpare);
2103
+ const axleTypesBinary = (filteredAxles || []).map((axle) => {
2104
+ let binaryRepresentation = 1;
2105
+ if (axle?.isLift) {
2106
+ binaryRepresentation |= 2;
2107
+ }
2108
+ if (axle?.isSteer) {
2109
+ binaryRepresentation |= 8;
2110
+ }
2111
+ if (axle?.isDrive) {
2112
+ binaryRepresentation |= 16;
2113
+ }
2114
+ return bridgeTools.decimalToHex(binaryRepresentation);
2115
+ });
2116
+ return axleTypesBinary;
2117
+ }
2118
+ function getEmptyAxles() {
2119
+ const defaultAxles = {};
2120
+ ___default.range(1, 16).forEach((n) => {
2121
+ const key = `axle${___default.padStart(n.toString(), 2, "0")}`;
2122
+ defaultAxles[key] = "00";
2123
+ });
2124
+ return defaultAxles;
2125
+ }
2126
+ function assignAxleTypes(defaultAxles, axleTypes) {
2127
+ for (let i = 0; i < Object.keys(defaultAxles).length && i < axleTypes?.length; i++) {
2128
+ const key = Object.keys(defaultAxles)[i];
2129
+ defaultAxles[key] = axleTypes[i];
2130
+ }
2131
+ return defaultAxles;
2132
+ }
2133
+ async function getAxlesWithAutolearnSettings(deviceId) {
2134
+ const defaultAxles = getEmptyAxles();
2135
+ const autolearnSettings = await bridgeCommands.getAutolearnSettings(deviceId);
2136
+ const pickedProperties = ___default.pick(autolearnSettings, [
2137
+ "checkingPeriod",
2138
+ "isAutolearnEnabled",
2139
+ "multiIdTimeout",
2140
+ "temperatureLimit"
2141
+ ]);
2142
+ Object.assign(defaultAxles, pickedProperties);
2143
+ return defaultAxles;
2144
+ }
2145
+ async function setAxleInfo(deviceId, tcVehicle) {
2146
+ if (!tcVehicle.axles?.length) throw new Error("Vehicle has no axles");
2147
+ const spareTyres = tcVehicle.tcTyres?.filter((t) => String(t.mountedOn?.positionId).endsWith("0")) || [];
2148
+ for (let axleIndex = 0; axleIndex < tcVehicle.axles.length; axleIndex++) {
2149
+ const tyres = [
2150
+ "00000000",
2151
+ "00000000",
2152
+ "00000000",
2153
+ "00000000",
2154
+ "00000000",
2155
+ "00000000",
2156
+ // LO
2157
+ "00000000",
2158
+ // LI
2159
+ "00000000",
2160
+ // null position
2161
+ "00000000",
2162
+ // RI
2163
+ "00000000",
2164
+ // RO
2165
+ "00000000",
2166
+ "00000000",
2167
+ "00000000",
2168
+ "00000000",
2169
+ "00000000"
2170
+ ];
2171
+ const axle = tcVehicle.axles[axleIndex];
2172
+ const spareTyre = spareTyres[axleIndex];
2173
+ if (spareTyre && spareTyre.tcTpmsSensor?.id) {
2174
+ tyres[7] = bridgeTools.convertSensorIdForBridge(spareTyre.tcTpmsSensor?.id);
2175
+ }
2176
+ const axleTyres = tcVehicle.tcTyres?.filter(
2177
+ (t) => t.mountedOn?.positionId ? bridgeTools.getPositionInfo(t.mountedOn?.positionId).axlePosition === axleIndex + 1 && spareTyre?.mountedOn?.positionId !== t.mountedOn?.positionId : false
2178
+ ) || [];
2179
+ for (let tyreIndex = 0; tyreIndex < axleTyres.length; tyreIndex++) {
2180
+ const sensorId = axleTyres[tyreIndex].tcTpmsSensor?.id;
2181
+ if (!sensorId) continue;
2182
+ if (axle.isSpare) continue;
2183
+ const isTwinTyre = axle.tyresCount === 4;
2184
+ let bridgeTyreIndex = 0;
2185
+ if (isTwinTyre && tyreIndex === 0) bridgeTyreIndex = 5;
2186
+ if (isTwinTyre && tyreIndex === 1 || !isTwinTyre && tyreIndex === 0) bridgeTyreIndex = 6;
2187
+ if (isTwinTyre && tyreIndex === 3) bridgeTyreIndex = 9;
2188
+ if (isTwinTyre && tyreIndex === 2 || !isTwinTyre && tyreIndex === 1) bridgeTyreIndex = 8;
2189
+ tyres[bridgeTyreIndex] = bridgeTools.convertSensorIdForBridge(sensorId);
2190
+ }
2191
+ if (!___default.inRange(axleIndex, 0, 16)) return;
2192
+ const numberArray = tyres.map((x) => x.match(/.{2}/g)?.reverse()).flat().map((n) => n ? Number.parseInt(n, 16) : 0);
2193
+ await bridgeCommands.setAxleInfo(deviceId, axleIndex, numberArray);
2194
+ }
2195
+ }
2196
+ async function setAxlesPressure(deviceId, axles) {
2197
+ let data = [];
2198
+ let bridgeAxlesPressureData;
2199
+ for (let index = 0; index < 15; index++) {
2200
+ let { targetPressure, maxTargetPressure, minTargetPressure, isSpare } = axles[index] || {};
2201
+ maxTargetPressure = bridgeTools.barToKpaByte(maxTargetPressure);
2202
+ minTargetPressure = bridgeTools.barToKpaByte(minTargetPressure);
2203
+ if (isSpare && axles[0]) {
2204
+ targetPressure = bridgeTools.barToKpaByte(axles[0].targetPressure);
2205
+ } else {
2206
+ targetPressure = bridgeTools.barToKpaByte(targetPressure);
2207
+ }
2208
+ if (!maxTargetPressure) {
2209
+ if (!bridgeAxlesPressureData) {
2210
+ bridgeAxlesPressureData = await bridgeCommands.getAxlesPressure(deviceId);
2211
+ }
2212
+ maxTargetPressure = bridgeAxlesPressureData[index * 3] || 255;
2213
+ }
2214
+ if (targetPressure > maxTargetPressure) {
2215
+ targetPressure = maxTargetPressure;
2216
+ } else if (targetPressure < minTargetPressure) {
2217
+ targetPressure = minTargetPressure;
2218
+ }
2219
+ data = data.concat([maxTargetPressure, minTargetPressure, targetPressure]);
2220
+ }
2221
+ await bridgeCommands.setAxlesPressure(deviceId, data);
2222
+ }
2223
+ async function setConfiguration(deviceId, bridgeConfiguration) {
2224
+ const currentBridgeConfiguration = await getConfiguration(deviceId);
2225
+ const configurationValidation = await bridgeConfigurationDifference(currentBridgeConfiguration, bridgeConfiguration);
2226
+ if (configurationValidation.differences.includes("workshopCANSettings")) {
2227
+ await bridgeCommands.setWorkshopCANSettings(deviceId, bridgeConfiguration.workshopCANSettings);
2228
+ }
2229
+ if (bridgeConfiguration.customerCANSettings) {
2230
+ const isFactory = await bridgeCommands.getCustomerCrcSettings(deviceId);
2231
+ if (configurationValidation.differences.includes("customerCANSettings") || isFactory) {
2232
+ await bridgeCommands.setCustomerCANSettings(deviceId, bridgeConfiguration.customerCANSettings);
2233
+ }
2234
+ }
2235
+ if (configurationValidation.differences.includes("customerPressureThresholds")) {
2236
+ await bridgeCommands.setCustomerPressureThresholds(deviceId, bridgeConfiguration.customerPressureThresholds);
2237
+ }
2238
+ if (configurationValidation.differences.includes("customerTemperatureThresholds")) {
2239
+ await bridgeCommands.setCustomerTemperatureThresholds(deviceId, bridgeConfiguration.customerTemperatureThresholds);
2240
+ }
2241
+ if (configurationValidation.differences.includes("customerImbalanceThresholds")) {
2242
+ await bridgeCommands.setCustomerImbalanceThresholds(deviceId, bridgeConfiguration.customerImbalanceThresholds);
2243
+ }
2244
+ if (configurationValidation.differences.includes("pressuresPerAxle")) {
2245
+ await bridgeCommands.setPressuresPerAxle(deviceId, bridgeConfiguration.pressuresPerAxle);
2246
+ }
2247
+ }
2248
+ async function setVehicle(deviceId, tcVehicle) {
2249
+ const currentTcVehicle = await getVehicle(deviceId);
2250
+ const vehicleValidation = bridgeVehiclesDifference(currentTcVehicle, tcVehicle);
2251
+ if (["bridgeId", "vehicleLayout", "vin"].some((n) => vehicleValidation.differences.includes(n))) {
2252
+ await setVehicleLayout(deviceId, tcVehicle);
2253
+ }
2254
+ if (["bridgeId", "sensorPosition"].some((n) => vehicleValidation.differences.includes(n))) {
2255
+ await setAxleInfo(deviceId, tcVehicle);
2256
+ }
2257
+ if (["bridgeId", "axlePressure"].some((n) => vehicleValidation.differences.includes(n))) {
2258
+ await setAxlesPressure(deviceId, tcVehicle.axles);
2259
+ }
2260
+ if (isVersionGreaterThan(deviceId, "0.9.7")) {
2261
+ if (["bridgeId", "axleType"].some((n) => vehicleValidation.differences.includes(n))) {
2262
+ const defaultAxles = await getAxlesWithAutolearnSettings(deviceId);
2263
+ const axleTypes = getVehicleAxlesTypes(tcVehicle);
2264
+ const updatedAxles = assignAxleTypes(defaultAxles, axleTypes);
2265
+ await bridgeCommands.setAutoLearnSettings(deviceId, updatedAxles);
2266
+ }
2267
+ }
2268
+ }
2269
+ async function getSensorReading(deviceId, positionId) {
2270
+ const value = await bridgeCommands.getSensorMeasurement(deviceId, positionId);
2271
+ const sensorId = value.slice(5, 9).reverse().map((s) => bridgeTools.decimalToHex(s).toUpperCase()).join("");
2272
+ const correctedSensorId = bridgeTools.convertSensorIdFromBridge(sensorId);
2273
+ return {
2274
+ date: Date.now() - value[14] * 2e3,
2275
+ sensorId: correctedSensorId,
2276
+ sensorStatus: value[12],
2277
+ eceStatus: value[13],
2278
+ pressureIssue: getPressureIssue(deviceId, value[13]),
2279
+ ...getPressureAndTemperatureReadingObjects(value[9], value[10])
2280
+ };
2281
+ }
2282
+ async function getVehicleReadings(deviceId, tcVehicle) {
2283
+ const readings = [];
2284
+ try {
2285
+ for (const vehicleTyre of tcVehicle.tcTyres) {
2286
+ if (vehicleTyre.tcTpmsSensor && vehicleTyre.mountedOn?.positionId) {
2287
+ if (!canCommunicateWith(deviceId)) return;
2288
+ const reading = await getSensorReading(deviceId, vehicleTyre.mountedOn.positionId);
2289
+ readings.push(reading);
2290
+ }
2291
+ }
2292
+ } catch (error) {
2293
+ console.error("getSensorReading failed: ", error);
2294
+ }
2295
+ return readings;
2296
+ }
2297
+ async function getAutolearnStatuses(deviceId, tcVehicle) {
2298
+ if (!isVersionGreaterThan(deviceId, "0.9.7"))
2299
+ throw new Error("getAutolearnStatuses is not supported by this firmware version");
2300
+ const statuses = await bridgeCommands.getAutolearnIdStatus(deviceId);
2301
+ const autolearnedStatuses = [];
2302
+ const statusEntries = Object.values(statuses);
2303
+ for (const tcTyre of tcVehicle.tcTyres) {
2304
+ if (!tcTyre.mountedOn?.positionId || tcTyre.mountedOn?.positionId % 10 === 0) continue;
2305
+ const axleIndex = Math.floor(tcTyre.mountedOn?.positionId / 100) - 1;
2306
+ const axleStatuses = statusEntries[axleIndex];
2307
+ const tyreIndex = getBridgeIndexFromPositionId(tcTyre.mountedOn.positionId, 7);
2308
+ const tyreStatus = axleStatuses.split("").reverse().join("")[tyreIndex];
2309
+ let autolearnedSensorId;
2310
+ if (["1", "4", "9"].includes(tyreStatus)) {
2311
+ const axleInfo = await bridgeCommands.getAxleInfo(deviceId, axleIndex);
2312
+ const axleData = axleInfo.map((i) => bridgeTools.decimalToHex(i).toUpperCase());
2313
+ const joinedAxleData = Array.from(
2314
+ { length: axleData.length / 4 },
2315
+ (_2, index) => axleData[4 * index + 3] + axleData[4 * index + 2] + axleData[4 * index + 1] + axleData[4 * index]
2316
+ );
2317
+ autolearnedSensorId = bridgeTools.convertSensorIdFromBridge(joinedAxleData[tyreIndex]);
2318
+ }
2319
+ autolearnedStatuses.push({
2320
+ positionId: tcTyre.mountedOn.positionId,
2321
+ autolearnedSensorId,
2322
+ autolearnedStatus: getAutolearnStatusName(tyreStatus)
2323
+ });
2324
+ }
2325
+ return autolearnedStatuses;
2326
+ }
2327
+ function getAutolearnStatusName(statusId) {
2328
+ if (["2", "A"].includes(statusId)) {
2329
+ return "error";
2330
+ }
2331
+ if (statusId === "4") {
2332
+ return "known";
2333
+ }
2334
+ if (["1", "9"].includes(statusId)) {
2335
+ return "unknown";
2336
+ }
2337
+ if (statusId === "0") {
2338
+ return "default";
2339
+ }
2340
+ throw new Error("Unknown autolearn status");
2341
+ }
2342
+ async function resetAutolearnStatuses(deviceId, positionIds) {
2343
+ if (!isVersionGreaterThan(deviceId, "0.9.7"))
2344
+ throw new Error("resetAutolearnStatuses is not supported by this firmware version");
2345
+ const statuses = await bridgeCommands.getAutolearnIdStatus(deviceId);
2346
+ const statusesKeys = Object.keys(statuses);
2347
+ const sortedByAxle = ___default.groupBy(positionIds, (p) => Math.floor(p / 100));
2348
+ for (const [axleIndex, axlePositionIds] of Object.entries(sortedByAxle)) {
2349
+ const statusesAxleIndex = Number(axleIndex) - 1;
2350
+ for (const positionId of axlePositionIds) {
2351
+ const bitIndex = getBridgeIndexFromPositionId(positionId, 7);
2352
+ let status = statuses[statusesKeys[statusesAxleIndex]].split("").reverse().join("");
2353
+ status = `${status.substring(0, bitIndex)}0${status.substring(bitIndex + 1)}`;
2354
+ statuses[statusesKeys[statusesAxleIndex]] = status.split("").reverse().join("");
2355
+ }
2356
+ }
2357
+ await bridgeCommands.setAutolearnIdStatus(deviceId, statuses);
2358
+ }
2359
+ function getBridgeIndexFromPositionId(positionId, centerIndex = 8) {
2360
+ const halfPoint = positionId % 10 * 6;
2361
+ const tyrePosition = positionId % 100;
2362
+ const result = (tyrePosition - halfPoint) / 10 - (tyrePosition <= halfPoint ? 1 : 0);
2363
+ const bitIndex = centerIndex + result;
2364
+ return bitIndex;
2365
+ }
2366
+ function getPressureAndTemperatureReadingObjects(rawPressure, rawTemperature) {
2367
+ if (rawTemperature === 255 || rawPressure === 0) {
2368
+ return {
2369
+ pressure: void 0,
2370
+ temperature: void 0
2371
+ };
2372
+ }
2373
+ const ambientTemperature = 20;
2374
+ const measuredTemperature = rawTemperature - 40;
2375
+ const measuredPressureAbsolute = rawPressure / 19.753086;
2376
+ const compensatedPressureAbsolute = measuredPressureAbsolute * (273 + ambientTemperature) / (273 + measuredTemperature);
2377
+ return {
2378
+ pressure: {
2379
+ raw: rawPressure,
2380
+ meas: Math.max(0, measuredPressureAbsolute - 1),
2381
+ bar: Math.max(0, compensatedPressureAbsolute - 1)
2382
+ },
2383
+ temperature: { raw: rawTemperature, amb: ambientTemperature, celsius: measuredTemperature }
2384
+ };
2385
+ }
2386
+ function getPressureIssue(deviceId, eceStatus) {
2387
+ if (eceStatus === 254) {
2388
+ return {
2389
+ date: Date.now(),
2390
+ tcIssue: {
2391
+ id: "outdatedCritical"
2392
+ },
2393
+ type: "outdated",
2394
+ severity: "critical"
2395
+ };
2396
+ }
2397
+ const usesNewValues = isVersionGreaterThan(deviceId, "0.8.F");
2398
+ if (eceStatus === (usesNewValues ? 3 : 4)) {
2399
+ return {
2400
+ date: Date.now(),
2401
+ tcIssue: {
2402
+ id: "overinflationCritical"
2403
+ },
2404
+ type: "over_inflated",
2405
+ severity: "critical"
2406
+ };
2407
+ }
2408
+ if (eceStatus === (usesNewValues ? 1 : 3)) {
2409
+ return {
2410
+ date: Date.now(),
2411
+ tcIssue: {
2412
+ id: "overinflationWarning"
2413
+ },
2414
+ severity: "warning",
2415
+ type: "over_inflated"
2416
+ };
2417
+ }
2418
+ if (eceStatus === (usesNewValues ? 4 : 2)) {
2419
+ return {
2420
+ date: Date.now(),
2421
+ tcIssue: {
2422
+ id: "underinflationCritical"
2423
+ },
2424
+ severity: "critical",
2425
+ type: "under_inflated"
2426
+ };
2427
+ }
2428
+ if (eceStatus === (usesNewValues ? 2 : 1)) {
2429
+ return {
2430
+ date: Date.now(),
2431
+ tcIssue: {
2432
+ id: "underinflationWarning"
2433
+ },
2434
+ severity: "warning",
2435
+ type: "under_inflated"
2436
+ };
2437
+ }
2438
+ }
2439
+ async function discoverOtaBridge(deviceId, reportStatus) {
2440
+ for (let i = 0; i < 200; i++) {
2441
+ const msg = "Discovering OTA bridge...";
2442
+ reportStatus(msg, i / 200);
2443
+ const d = store.devices[deviceId];
2444
+ if (d) {
2445
+ if (d.type === "bridgeOta") return d;
2446
+ if (d.type === "bridge" && i > 100) throw new Error("Bridge has not switched to OTA mode. Please, try again.".t());
2447
+ }
2448
+ await toolsSvc.delay(200);
2449
+ }
2450
+ throw new Error("Cannot detect the bridge");
2451
+ }
2452
+ async function waitForBridgeToReconnect(deviceId, reportStatus) {
2453
+ let receivedOta = false;
2454
+ for (let i = 0; i < 200; i++) {
2455
+ reportStatus("Waiting for bridge to reconnect...", i / 200);
2456
+ const d = store.devices[deviceId];
2457
+ if (d) {
2458
+ if (d.type === "bridge") return d;
2459
+ if (d.type === "bridgeOta" && i > 50) receivedOta = true;
2460
+ }
2461
+ await toolsSvc.delay(200);
2462
+ }
2463
+ if (receivedOta) {
2464
+ throw new Error("Bridge has not switched from OTA mode. Please, try again.");
2465
+ }
2466
+ throw new Error("Cannot reconnect to the bridge after firmware update");
2467
+ }
2468
+ async function getVehicle(deviceId) {
2469
+ if (!canCommunicateWith(deviceId)) throw new Error("Not connected to any device");
2470
+ const deviceData = getDeviceData(deviceId);
2471
+ const tcVehicle = {
2472
+ registrationNumber: "",
2473
+ vin: deviceData.vin,
2474
+ axles: [],
2475
+ tcTyres: [],
2476
+ tcBridge: { id: deviceData.bridgeId }
2477
+ };
2478
+ await assignAxles(deviceId, tcVehicle);
2479
+ await assignTyres(deviceId, tcVehicle);
2480
+ return tcVehicle;
2481
+ }
2482
+ async function assignAxles(deviceId, tcVehicle) {
2483
+ const result = await bridgeCommands.getVehicleLayout(deviceId);
2484
+ const data = result.map((x) => bridgeTools.decimalToHex(x));
2485
+ const joinedData = Array.from({ length: data.length / 2 }, (_2, index) => data[2 * index + 1] + data[2 * index]);
2486
+ const activeAxles = joinedData.slice(0, 15).filter((x) => Number(`0x${x}`));
2487
+ const axlesPressureData = await bridgeCommands.getAxlesPressure(deviceId);
2488
+ let axleTypesBytes;
2489
+ if (isVersionGreaterThan(deviceId, "0.9.7")) {
2490
+ const axleTypes = await bridgeCommands.getAutolearnSettings(deviceId);
2491
+ axleTypesBytes = bridgeTools.convertAutoLearnObjectToBinary(axleTypes);
2492
+ }
2493
+ for (let index = 0; index < activeAxles.length; index++) {
2494
+ let isSteer = false;
2495
+ let isDrive = false;
2496
+ let isLift = false;
2497
+ if (axleTypesBytes) {
2498
+ const axleKey = `axle${String(index + 1).padStart(2, "0")}`;
2499
+ const tempAxle = axleTypesBytes[axleKey];
2500
+ isLift = tempAxle ? tempAxle.charAt(6) === "1" : false;
2501
+ isSteer = tempAxle ? tempAxle.charAt(4) === "1" : false;
2502
+ isDrive = tempAxle ? tempAxle.charAt(3) === "1" : false;
2503
+ }
2504
+ const tyreCount = Number.parseInt(activeAxles[index], 16).toString(2).split("").filter((x) => x === "1").length;
2505
+ if (!tyreCount) continue;
2506
+ const hasSpareTyre = tyreCount % 2 === 1;
2507
+ const axle = { tyresCount: hasSpareTyre ? tyreCount - 1 : tyreCount, isSteer, isDrive, isLift };
2508
+ const axleWithLimits = await assignAxlePressureLimits(deviceId, tcVehicle, axle, index, axlesPressureData);
2509
+ tcVehicle.axles.push(axleWithLimits);
2510
+ if (hasSpareTyre) {
2511
+ const spareAxle = { tyresCount: 0, isSpare: true };
2512
+ const spareAxleWithLimits = await assignAxlePressureLimits(deviceId, tcVehicle, spareAxle, 0, axlesPressureData);
2513
+ tcVehicle.axles.push(spareAxleWithLimits);
2514
+ }
2515
+ }
2516
+ tcVehicle.axles = ___default.sortBy(tcVehicle.axles, (a) => a.isSpare ? 1 : 0);
2517
+ }
2518
+ async function assignTyres(deviceId, tcVehicle) {
2519
+ const mountedTyres = tcVehicle.axles.filter((a) => !a.isSpare);
2520
+ for (let index = 0; index < tcVehicle.axles.length; index++) {
2521
+ const axle = tcVehicle.axles[index];
2522
+ const axleInfo = await bridgeCommands.getAxleInfo(deviceId, axle.isSpare ? index - mountedTyres.length : index) || [];
2523
+ const axleData = axleInfo.map((i) => bridgeTools.decimalToHex(i).toUpperCase());
2524
+ const joinedAxleData = Array.from(
2525
+ { length: axleData.length / 4 },
2526
+ (_2, index2) => axleData[4 * index2 + 3] + axleData[4 * index2 + 2] + axleData[4 * index2 + 1] + axleData[4 * index2]
2527
+ );
2528
+ if (axle.isSpare) {
2529
+ const spareTyrePositionId = getPositionId(index + 1, 1, 0, true);
2530
+ const spareTyre = createNewTyre(spareTyrePositionId);
2531
+ if (Number.parseInt(joinedAxleData[7], 16)) {
2532
+ spareTyre.tcTpmsSensor = {
2533
+ id: bridgeTools.convertSensorIdFromBridge(joinedAxleData[7])
2534
+ // 7th place is reserved for spare tyres
2535
+ };
2536
+ }
2537
+ tcVehicle.tcTyres.push(spareTyre);
2538
+ continue;
2539
+ }
2540
+ const hasTwoTyres = ___default.inRange(axle.tyresCount, 2, 4);
2541
+ const hasFourTyres = ___default.inRange(axle.tyresCount, 4, 6);
2542
+ let sensorIndexes = [];
2543
+ if (hasTwoTyres) sensorIndexes = [6, 8];
2544
+ if (hasFourTyres) sensorIndexes = [5, 6, 8, 9];
2545
+ for (let tyreIndex = 0; tyreIndex < sensorIndexes.length; tyreIndex++) {
2546
+ const tyrePositionId = getPositionId(index + 1, tyreIndex + 1, axle.tyresCount);
2547
+ const tyre = createNewTyre(tyrePositionId);
2548
+ if (Number.parseInt(joinedAxleData[sensorIndexes[tyreIndex]], 16)) {
2549
+ tyre.tcTpmsSensor = {
2550
+ id: bridgeTools.convertSensorIdFromBridge(joinedAxleData[sensorIndexes[tyreIndex]])
2551
+ };
2552
+ }
2553
+ tcVehicle.tcTyres.push(tyre);
2554
+ }
2555
+ }
2556
+ }
2557
+ async function assignAxlePressureLimits(deviceId, tcVehicle, tcVehicleAxle, axleIndex, bridgeAxlesPressureData) {
2558
+ const axlesPressureData = bridgeAxlesPressureData ?? await bridgeCommands.getAxlesPressure(deviceId);
2559
+ if (axlesPressureData[axleIndex * 3]) {
2560
+ tcVehicleAxle.maxTargetPressure = bridgeTools.kpaByteToBar(axlesPressureData[axleIndex * 3]);
2561
+ }
2562
+ if (axlesPressureData[axleIndex * 3 + 1]) {
2563
+ tcVehicleAxle.minTargetPressure = bridgeTools.kpaByteToBar(axlesPressureData[axleIndex * 3 + 1]);
2564
+ }
2565
+ if (axlesPressureData[axleIndex * 3 + 2]) {
2566
+ tcVehicleAxle.targetPressure = bridgeTools.kpaByteToBar(
2567
+ axlesPressureData[axleIndex * 3 + 2],
2568
+ void 0
2569
+ );
2570
+ }
2571
+ return tcVehicleAxle;
2572
+ }
2573
+ function createNewTyre(positionId) {
2574
+ return {
2575
+ mountedOn: { positionId },
2576
+ serialNumber: `TYRE-${positionId}`
2577
+ };
2578
+ }
2579
+
2580
+ const bridge = {
2581
+ async connect(deviceId) {
2582
+ await promiseQueue$1.clearQueue("Previous pending commands aborted");
2583
+ const bridgeMeta = deviceMeta.bridge;
2584
+ await bluetooth.connect(deviceId, this.disconnect);
2585
+ await ble.requestMtu(deviceId, deviceMeta.bridge.mtu);
2586
+ await ble.startNotification(
2587
+ deviceId,
2588
+ bridgeMeta.communication.serviceId,
2589
+ bridgeMeta.communication.characteristicId,
2590
+ (notification) => promiseQueue$1.processMessage(deviceId, notification),
2591
+ (error) => console.log("Nofit Error", error)
2592
+ );
2593
+ await bridgeCommands.sendPinCommand(deviceId);
2594
+ store.deviceState[deviceId] = "paired";
2595
+ },
2596
+ async disconnect(deviceId) {
2597
+ store.deviceState[deviceId] = "disconnecting";
2598
+ await promiseQueue$1.clearQueue("Previous pending commands aborted");
2599
+ await bluetooth.disconnect(deviceId);
2600
+ store.deviceState[deviceId] = "disconnected";
2601
+ },
2602
+ getVehicle: bridgeService.getVehicle,
2603
+ setVehicle: bridgeService.setVehicle,
2604
+ getConfiguration: bridgeService.getConfiguration,
2605
+ setConfiguration: bridgeService.setConfiguration,
2606
+ getSensorReading: bridgeService.getSensorReading,
2607
+ getVehicleReadings: bridgeService.getVehicleReadings,
2608
+ getAutolearnStatuses: bridgeService.getAutolearnStatuses,
2609
+ resetAutolearnStatuses: bridgeService.resetAutolearnStatuses,
2610
+ updateFirmware: bridgeService.updateFirmware
2611
+ };
2612
+
2613
+ const flexiGaugeTpmsMeta = deviceMeta.flexiGaugeTpms;
2614
+ const flexiGaugeTpmsCommands = {
2615
+ startTpmsScan,
2616
+ getBattery
2617
+ };
2618
+ async function startTpmsScan(deviceId) {
2619
+ if (!canCommunicateWith(deviceId)) throw new Error("Flexi Gauge not connected");
2620
+ const scanMsg = stringToArrayBuffer("*TPMS ON TIME=10\r\n");
2621
+ return bluetooth.write(
2622
+ deviceId,
2623
+ flexiGaugeTpmsMeta.communication.serviceId,
2624
+ flexiGaugeTpmsMeta.communication.characteristicId,
2625
+ scanMsg
2626
+ );
2627
+ }
2628
+ async function getBattery(deviceId) {
2629
+ const batteryValue = await bluetooth.read(
2630
+ deviceId,
2631
+ flexiGaugeTpmsMeta.battery.serviceId,
2632
+ flexiGaugeTpmsMeta.battery.characteristicId
2633
+ );
2634
+ const battery = Array.from(new Uint8Array(batteryValue))[0];
2635
+ return battery;
2636
+ }
2637
+
2638
+ let sensorReadings = [];
2639
+ let treadDepthCallback;
2640
+ let buttonCallback;
2641
+ let tpmsCallback;
2642
+ const capabilities = [
2643
+ {
2644
+ id: "td",
2645
+ processFn: processTreadDepth,
2646
+ regex: /^\*TD.*/
2647
+ },
2648
+ {
2649
+ id: "button",
2650
+ processFn: processButtonPress,
2651
+ regex: /^\*[UDLRC] $/
2652
+ },
2653
+ {
2654
+ id: "tpms",
2655
+ processFn: processTpms,
2656
+ regex: /^\*TPMS.*/
2657
+ }
2658
+ ];
2659
+ const flexiGaugeTpmsService = {
2660
+ startTpmsScan(deviceId) {
2661
+ flexiGaugeTpmsCommands.startTpmsScan(deviceId);
2662
+ },
2663
+ getBattery(deviceId) {
2664
+ return flexiGaugeTpmsCommands.getBattery(deviceId);
2665
+ },
2666
+ onTreadDepth(callback) {
2667
+ treadDepthCallback = callback;
2668
+ },
2669
+ onButton(callback) {
2670
+ buttonCallback = callback;
2671
+ },
2672
+ onTpms(callback) {
2673
+ tpmsCallback = callback;
2674
+ },
2675
+ icon: "icon-flexigauge",
2676
+ processMessage(deviceId, message) {
2677
+ const numberArray = Array.from(new Uint8Array(message));
2678
+ const stringFromBytes = String.fromCharCode.apply(null, numberArray).replace("\r\n", "");
2679
+ for (const capability of capabilities) {
2680
+ if (capability.regex.test(stringFromBytes)) {
2681
+ return capability.processFn(stringFromBytes);
2682
+ }
2683
+ }
2684
+ }
2685
+ };
2686
+ function processTreadDepth(value) {
2687
+ const treadDepth = value.match(/\d+/)[0];
2688
+ const convertedValue = treadDepth / 1e3;
2689
+ treadDepthCallback(convertedValue);
2690
+ }
2691
+ function processButtonPress(value) {
2692
+ const buttonName = value.match(/\*(.)/);
2693
+ if (!buttonName || !buttonName[1]) throw new Error("Unrecognized button");
2694
+ buttonCallback(buttonName[1]);
2695
+ }
2696
+ function processTpms(value) {
2697
+ const groupedValue = value.split(" ");
2698
+ const type = groupedValue[1];
2699
+ if (type === "ISON") {
2700
+ sensorReadings = [];
2701
+ }
2702
+ if (type === "DATA") {
2703
+ const reading = {};
2704
+ for (const group of groupedValue) {
2705
+ if (!group.includes("=")) continue;
2706
+ const groupString = group.replace(",", "");
2707
+ const groupSeparated = groupString.split("=");
2708
+ reading[groupSeparated[0]] = groupSeparated[1];
2709
+ }
2710
+ sensorReadings.push(reading);
2711
+ }
2712
+ if (type === "ENDSCANNING") {
2713
+ if (!sensorReadings.length) {
2714
+ return notify.create({
2715
+ type: "info",
2716
+ message: "No sensors found"
2717
+ });
2718
+ }
2719
+ const strongestReading = sensorReadings.reduce((accumulator, currentReading) => {
2720
+ return Number(currentReading.Rssi) > Number(accumulator.Rssi) ? currentReading : accumulator;
2721
+ }, sensorReadings[0]);
2722
+ tpmsCallback(strongestReading);
2723
+ }
2724
+ }
2725
+
2726
+ const flexiGaugeTpms = {
2727
+ async connect(deviceId) {
2728
+ const bridgeMeta = deviceMeta.flexiGaugeTpms;
2729
+ await bluetooth.connect(deviceId, this.disconnect);
2730
+ await ble.startNotification(
2731
+ deviceId,
2732
+ bridgeMeta.communication.serviceId,
2733
+ bridgeMeta.communication.characteristicId,
2734
+ (notification) => flexiGaugeTpmsService.processMessage(deviceId, notification),
2735
+ (error) => console.log("Nofit Error", error)
2736
+ );
2737
+ store.deviceState[deviceId] = "paired";
2738
+ },
2739
+ async disconnect(deviceId) {
2740
+ store.deviceState[deviceId] = "disconnecting";
2741
+ await bluetooth.disconnect(deviceId);
2742
+ store.deviceState[deviceId] = "disconnected";
2743
+ },
2744
+ onTreadDepth(callback) {
2745
+ flexiGaugeTpmsService.onTreadDepth(callback);
2746
+ },
2747
+ onButtonPress(callback) {
2748
+ flexiGaugeTpmsService.onButton(callback);
2749
+ },
2750
+ onTpms(callback) {
2751
+ flexiGaugeTpmsService.onTpms(callback);
2752
+ },
2753
+ getBattery: flexiGaugeTpmsService.getBattery,
2754
+ startTpmsScan: flexiGaugeTpmsService.startTpmsScan
2755
+ };
2756
+
2757
+ const bridgeSimulator = {
2758
+ async connect(bridge) {
2759
+ bridge.status = "connecting";
2760
+ await toolsSvc.delay(100);
2761
+ bridge.status = "connected";
2762
+ },
2763
+ async disconnect(bridge) {
2764
+ bridge.status = "disconnecting";
2765
+ await toolsSvc.delay(100);
2766
+ bridge.status = void 0;
2767
+ },
2768
+ createSimulatedBridge(initialData) {
2769
+ return {
2770
+ type: "bridge",
2771
+ id: "bridge1",
2772
+ rssi: -50,
2773
+ bridgeId: "FFFFAABB",
2774
+ vin: "VIN1",
2775
+ name: "Bridge1",
2776
+ simulatorData: {},
2777
+ advertisingData: {
2778
+ configVersion: 1,
2779
+ macAddress: [1, 2, 3, 4, 5, 6],
2780
+ randomAdvNumber: [0, 0, 0, 0, 0, 0, 0, 0],
2781
+ vinNum: [86, 87, 88, 89, 90, 91, 92, 93],
2782
+ fwVersion: "0.7.C",
2783
+ timeFromStart: 255
2784
+ },
2785
+ ...initialData
2786
+ };
2787
+ }
2788
+ // simulateWriteToDevice(
2789
+ // device: SimulatedBluetoothDeviceBridge,
2790
+ // serviceId: string,
2791
+ // characteristicId: string,
2792
+ // value: ArrayBuffer,
2793
+ // ) {
2794
+ // const command = new Uint8Array(value)
2795
+ // if (!command.length) {
2796
+ // console.warn('[BT Simulator] Empty write command received')
2797
+ // return
2798
+ // }
2799
+ // const [_sl, _msgType, size, commandId, subCommandId, ...payloadBytes] = command
2800
+ // const payload = payloadBytes.slice(0, size - 2)
2801
+ // const _macAddress = payload.slice(0, 6)
2802
+ // const _paddedTimestamp = payload.slice(6, 14)
2803
+ // const _crc = payload.slice(-4)
2804
+ // const payloadData = payload.slice(14, -4)
2805
+ // const responseHeader = [
2806
+ // securityLevelIds.tirecheck,
2807
+ // messageTypeIds.response,
2808
+ // 0,
2809
+ // commandId,
2810
+ // subCommandId,
2811
+ // ..._.range(14).map((x) => 0), // mac address and timestamp
2812
+ // ]
2813
+ // if (!devtoolsStore.simulatedBluetoothDeviceHandlers[device.id])
2814
+ // console.warn(`[Bluetooth] Device ${device.id} is disconnected`)
2815
+ // const simulateReadFromDevice = (data: ArrayBuffer) => {
2816
+ // const dataToSend = new Uint8Array(data)
2817
+ // devtoolsStore.simulatedBluetoothDeviceHandlers[device.id]?.(
2818
+ // new Uint8Array([...dataToSend, 0, 0, 0, 0, 0, 0, 0, 0]).buffer,
2819
+ // ) // Append CRC to the data
2820
+ // }
2821
+ // const context: BridgeSimulatorContext = { device, responseHeader, payloadData, simulateReadFromDevice }
2822
+ // if (commandId === commandIds.keepAlive) {
2823
+ // context.responseHeader = [
2824
+ // securityLevelIds.tirecheck,
2825
+ // messageTypeIds.response,
2826
+ // 2,
2827
+ // commandIds.keepAliveResponse,
2828
+ // subCommandId,
2829
+ // ...Array.from({ length: 4 }).fill(0),
2830
+ // ]
2831
+ // return simulateKeepalive(context)
2832
+ // }
2833
+ // if (commandId === commandIds.pin) {
2834
+ // return simulatePinResponse(context)
2835
+ // }
2836
+ // if (commandId === commandIds.sensorMeasurement) {
2837
+ // return simulateReadSensorMeasurement(context, subCommandId)
2838
+ // }
2839
+ // if (subCommandId === subCommandIds.vehicleLayout) {
2840
+ // if (commandId === commandIds.readData) {
2841
+ // return simulateReadVehicleLayout(context)
2842
+ // }
2843
+ // if (commandId === commandIds.writeData) {
2844
+ // return simulateWriteVehicleLayout(context)
2845
+ // }
2846
+ // }
2847
+ // if (subCommandId === subCommandIds.pressurePerAxle) {
2848
+ // if (commandId === commandIds.readData) {
2849
+ // return simulateReadPressurePerAxle(context)
2850
+ // }
2851
+ // if (commandId === commandIds.writeData) {
2852
+ // return simulateWritePressurePerAxle(context)
2853
+ // }
2854
+ // }
2855
+ // if (subCommandId === subCommandIds.customerPressureThresholds) {
2856
+ // if (commandId === commandIds.readData) {
2857
+ // return simulateReadPressureThresholds(context)
2858
+ // }
2859
+ // if (commandId === commandIds.writeData) {
2860
+ // return simulateWritePressureThresholds(context)
2861
+ // }
2862
+ // }
2863
+ // if (_.inRange(subCommandId, subCommandIds.idsPerWheel(0), subCommandIds.idsPerWheel(16))) {
2864
+ // const axleIndex = subCommandId - subCommandIds.idsPerWheel(0)
2865
+ // if (commandId === commandIds.readData) {
2866
+ // return simulateReadSensorIdsPerWheel(axleIndex, context)
2867
+ // }
2868
+ // if (commandId === commandIds.writeData) {
2869
+ // return simulateWriteSensorIdsPerWheel(axleIndex, context)
2870
+ // }
2871
+ // }
2872
+ // if (
2873
+ // [
2874
+ // subCommandIds.rebootSubCommand,
2875
+ // subCommandIds.customerCanSettings,
2876
+ // subCommandIds.workshopCanSettings,
2877
+ // subCommandIds.bridgeConfiguration,
2878
+ // subCommandIds.customerTemperatureThresholds,
2879
+ // subCommandIds.customerImbalanceThresholds,
2880
+ // ].includes(subCommandId)
2881
+ // ) {
2882
+ // const { responseHeader, simulateReadFromDevice } = context
2883
+ // return simulateReadFromDevice?.(new Uint8Array([...responseHeader]).buffer)
2884
+ // }
2885
+ // console.error(`[Bluetooth simulator] Unknown command: 0x${commandId.toString(16)} 0x${subCommandId.toString(16)}`)
2886
+ // },
2887
+ };
2888
+
2889
+ let simulatedDevices = {};
2890
+ let areSimulatorMethodsInjected = false;
2891
+ let advertisingInterval;
2892
+ const simulator = {
2893
+ setSimulatedDevices(devices) {
2894
+ ensureSimulatorMethodsAreInjected();
2895
+ simulatedDevices = { ...devices };
2896
+ },
2897
+ addSimulatedDevice(device) {
2898
+ ensureSimulatorMethodsAreInjected();
2899
+ simulatedDevices[device.id] = device;
2900
+ },
2901
+ getSimulatedDevices() {
2902
+ return simulatedDevices;
2903
+ }
2904
+ };
2905
+ function ensureSimulatorMethodsAreInjected() {
2906
+ if (areSimulatorMethodsInjected) return;
2907
+ areSimulatorMethodsInjected = true;
2908
+ const scanDevices = bluetooth.scanDevices;
2909
+ bluetooth.scanDevices = async (...args) => {
2910
+ scanDevices(...args);
2911
+ if (advertisingInterval) {
2912
+ clearInterval(advertisingInterval);
2913
+ advertisingInterval = void 0;
2914
+ }
2915
+ advertisingInterval = toolsSvc.setIntervalImmediate(() => {
2916
+ for (const key in simulatedDevices) {
2917
+ deviceAdvertisingCallback?.(simulatedDevices[key]);
2918
+ }
2919
+ }, 2e3);
2920
+ };
2921
+ for (const key in bridge) {
2922
+ const fnName = key;
2923
+ const originalFn = bridge[fnName];
2924
+ bridge[fnName] = (device, ...args) => {
2925
+ const deviceId = device.id;
2926
+ const simulatedDevice = simulatedDevices[deviceId];
2927
+ if (simulatedDevices) return bridgeSimulator[fnName](simulatedDevice, ...args);
2928
+ else return originalFn(device, ...args);
2929
+ };
2930
+ }
2931
+ }
2932
+
2933
+ class BridgeTcVehicleAxle {
2934
+ targetPressure;
2935
+ tyresCount = 2;
2936
+ isSteer;
2937
+ isSpare;
2938
+ isDrive;
2939
+ isLift;
2940
+ spacesBelow;
2941
+ maxTargetPressure;
2942
+ minTargetPressure;
416
2943
  }
417
2944
 
418
2945
  function createTirecheckDeviceSdk(bleImplementation) {
419
2946
  setBleImplementation(bleImplementation);
420
2947
  return {
421
- bluetooth
2948
+ /** Generic methods common for all devices */
2949
+ bluetooth,
2950
+ /** Methods for working with Tirecheck CAN Bridge */
2951
+ bridge,
2952
+ /** Methods for working with Tirecheck CAN Bridge in OTA mode */
2953
+ bridgeOta,
2954
+ /** Methods for working with Tirecheck TPMS FlexiGauge */
2955
+ flexiGaugeTpms,
2956
+ /** Allows simulating devices without actually using bluetooth */
2957
+ simulator
422
2958
  };
423
2959
  }
424
2960
 
2961
+ exports.BridgeTcVehicleAxle = BridgeTcVehicleAxle;
425
2962
  exports.createTirecheckDeviceSdk = createTirecheckDeviceSdk;