brilliantsole 0.0.26 → 0.0.28

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.
Files changed (215) hide show
  1. package/README.md +16 -10
  2. package/assets/3d/anchor.glb +0 -0
  3. package/assets/3d/coin.glb +0 -0
  4. package/assets/3d/glasses.glb +0 -0
  5. package/assets/3d/rightHand.glb +0 -0
  6. package/assets/audio/bounceMedium.wav +0 -0
  7. package/assets/audio/bounceStrong.wav +0 -0
  8. package/assets/audio/bounceWeak.wav +0 -0
  9. package/assets/audio/coin.wav +0 -0
  10. package/assets/audio/getUp.wav +0 -0
  11. package/assets/audio/grab.wav +0 -0
  12. package/assets/audio/kick.wav +0 -0
  13. package/assets/audio/platterFadeIn old.wav +0 -0
  14. package/assets/audio/platterFadeIn.wav +0 -0
  15. package/assets/audio/platterFadeOut.wav +0 -0
  16. package/assets/audio/punch.wav +0 -0
  17. package/assets/audio/punchSqueak.wav +0 -0
  18. package/assets/audio/purr.wav +0 -0
  19. package/assets/audio/purrFadeOut.wav +0 -0
  20. package/assets/audio/release.wav +0 -0
  21. package/assets/audio/splat.wav +0 -0
  22. package/assets/audio/stomp.wav +0 -0
  23. package/assets/images/ukaton-pressure-0.svg +9 -0
  24. package/assets/images/ukaton-pressure-1.svg +9 -0
  25. package/assets/images/ukaton-pressure-10.svg +9 -0
  26. package/assets/images/ukaton-pressure-11.svg +9 -0
  27. package/assets/images/ukaton-pressure-12.svg +9 -0
  28. package/assets/images/ukaton-pressure-13.svg +9 -0
  29. package/assets/images/ukaton-pressure-14.svg +9 -0
  30. package/assets/images/ukaton-pressure-15.svg +9 -0
  31. package/assets/images/ukaton-pressure-2.svg +9 -0
  32. package/assets/images/ukaton-pressure-3.svg +9 -0
  33. package/assets/images/ukaton-pressure-4.svg +9 -0
  34. package/assets/images/ukaton-pressure-5.svg +9 -0
  35. package/assets/images/ukaton-pressure-6.svg +9 -0
  36. package/assets/images/ukaton-pressure-7.svg +9 -0
  37. package/assets/images/ukaton-pressure-8.svg +9 -0
  38. package/assets/images/ukaton-pressure-9.svg +9 -0
  39. package/assets/images/ukaton-right-insole.svg +798 -0
  40. package/build/brilliantsole.cjs +2870 -882
  41. package/build/brilliantsole.cjs.map +1 -1
  42. package/build/brilliantsole.js +2477 -782
  43. package/build/brilliantsole.js.map +1 -1
  44. package/build/brilliantsole.ls.js +2260 -592
  45. package/build/brilliantsole.ls.js.map +1 -1
  46. package/build/brilliantsole.min.js +1 -1
  47. package/build/brilliantsole.min.js.map +1 -1
  48. package/build/brilliantsole.module.d.ts +302 -116
  49. package/build/brilliantsole.module.js +2468 -782
  50. package/build/brilliantsole.module.js.map +1 -1
  51. package/build/brilliantsole.module.min.d.ts +302 -116
  52. package/build/brilliantsole.module.min.js +1 -1
  53. package/build/brilliantsole.module.min.js.map +1 -1
  54. package/build/brilliantsole.node.module.d.ts +295 -113
  55. package/build/brilliantsole.node.module.js +2860 -882
  56. package/build/brilliantsole.node.module.js.map +1 -1
  57. package/build/dts/BS-output.d.ts +10 -0
  58. package/build/dts/BS.d.ts +21 -9
  59. package/build/dts/CameraManager.d.ts +72 -0
  60. package/build/dts/Device.d.ts +53 -16
  61. package/build/dts/DeviceInformationManager.d.ts +4 -4
  62. package/build/dts/DeviceManager.d.ts +3 -0
  63. package/build/dts/FileTransferManager.d.ts +18 -8
  64. package/build/dts/InformationManager.d.ts +8 -5
  65. package/build/dts/TfliteManager.d.ts +22 -2
  66. package/build/dts/WifiManager.d.ts +61 -0
  67. package/build/dts/connection/BaseConnectionManager.d.ts +37 -3
  68. package/build/dts/connection/ClientConnectionManager.d.ts +11 -2
  69. package/build/dts/connection/bluetooth/BluetoothConnectionManager.d.ts +1 -0
  70. package/build/dts/connection/bluetooth/NobleConnectionManager.d.ts +3 -1
  71. package/build/dts/connection/bluetooth/WebBluetoothConnectionManager.d.ts +2 -0
  72. package/build/dts/connection/bluetooth/bluetoothUUIDs.d.ts +2 -2
  73. package/build/dts/connection/udp/UDPConnectionManager.d.ts +28 -0
  74. package/build/dts/connection/webSocket/WebSocketConnectionManager.d.ts +25 -0
  75. package/build/dts/devicePair/DevicePair.d.ts +14 -10
  76. package/build/dts/devicePair/DevicePairPressureSensorDataManager.d.ts +8 -4
  77. package/build/dts/devicePair/DevicePairSensorDataManager.d.ts +2 -2
  78. package/build/dts/scanner/BaseScanner.d.ts +4 -1
  79. package/build/dts/scanner/NobleScanner.d.ts +2 -1
  80. package/build/dts/sensor/MotionSensorDataManager.d.ts +5 -2
  81. package/build/dts/sensor/SensorDataManager.d.ts +5 -4
  82. package/build/dts/server/BaseClient.d.ts +6 -3
  83. package/build/dts/server/ServerUtils.d.ts +1 -1
  84. package/build/dts/server/websocket/WebSocketUtils.d.ts +1 -1
  85. package/build/dts/utils/CenterOfPressureHelper.d.ts +2 -2
  86. package/build/dts/utils/Console.d.ts +2 -0
  87. package/build/dts/utils/MathUtils.d.ts +2 -0
  88. package/build/dts/utils/ThrottleUtils.d.ts +2 -0
  89. package/build/dts/vibration/VibrationManager.d.ts +19 -2
  90. package/build/index.d.ts +299 -113
  91. package/build/index.node.d.ts +292 -110
  92. package/examples/3d/scene.html +19 -5
  93. package/examples/3d/script.js +90 -17
  94. package/examples/3d-generic/index.html +144 -0
  95. package/examples/3d-generic/script.js +266 -0
  96. package/examples/balance/script.js +2 -1
  97. package/examples/basic/index.html +232 -18
  98. package/examples/basic/script.js +746 -106
  99. package/examples/bottango/index.html +11 -1
  100. package/examples/bottango/script.js +2 -2
  101. package/examples/center-of-pressure/index.html +114 -114
  102. package/examples/center-of-pressure/script.js +1 -1
  103. package/examples/device-pair/index.html +58 -58
  104. package/examples/device-pair/script.js +12 -8
  105. package/examples/edge-impulse/script.js +135 -44
  106. package/examples/edge-impulse-test/README.md +11 -0
  107. package/examples/edge-impulse-test/edge-impulse-standalone.js +7228 -0
  108. package/examples/edge-impulse-test/edge-impulse-standalone.wasm +0 -0
  109. package/examples/edge-impulse-test/index.html +75 -0
  110. package/examples/edge-impulse-test/run-impulse.js +135 -0
  111. package/examples/edge-impulse-test/script.js +200 -0
  112. package/examples/gloves/edge-impulse-standalone.js +7228 -0
  113. package/examples/gloves/edge-impulse-standalone.wasm +0 -0
  114. package/examples/gloves/index.html +119 -0
  115. package/examples/gloves/run-impulse.js +135 -0
  116. package/examples/gloves/scene.html +124 -0
  117. package/examples/gloves/script.js +931 -0
  118. package/examples/graph/index.html +11 -1
  119. package/examples/graph/script.js +94 -37
  120. package/examples/pressure/index.html +180 -12
  121. package/examples/pressure/script.js +144 -7
  122. package/examples/punch/index.html +135 -0
  123. package/examples/punch/punch.tflite +0 -0
  124. package/examples/punch/script.js +169 -0
  125. package/examples/recording/index.html +191 -183
  126. package/examples/server/index.html +109 -23
  127. package/examples/server/script.js +322 -111
  128. package/examples/ukaton-firmware-update/index.html +20 -0
  129. package/examples/ukaton-firmware-update/manifest.json +11 -0
  130. package/examples/ukaton-firmware-update/merged-firmware.bin +0 -0
  131. package/examples/utils/aframe/aframe-master.min.js +2 -0
  132. package/examples/utils/aframe/bs-vibration.js +150 -0
  133. package/examples/utils/aframe/force-pushable.js +80 -0
  134. package/examples/utils/aframe/grabbable-anchor.js +46 -0
  135. package/examples/utils/aframe/grabbable-listener.js +31 -0
  136. package/examples/utils/aframe/grabbable-physics-body.js +190 -0
  137. package/examples/utils/aframe/grow-shrink.js +25 -0
  138. package/examples/utils/aframe/hand-punch.js +119 -0
  139. package/examples/utils/aframe/my-obb-collider.js +293 -0
  140. package/examples/utils/aframe/occlude-hand-tracking-controls.js +47 -0
  141. package/examples/utils/aframe/occlude-mesh.js +42 -0
  142. package/examples/utils/aframe/palm-up-detector.js +47 -0
  143. package/examples/utils/aframe/shadow-material.js +20 -0
  144. package/examples/utils/aframe/soft-shadow-light.js +9 -0
  145. package/examples/webxr/script.js +3 -3
  146. package/examples/webxr-2/assets/3d/soccerBall.glb +0 -0
  147. package/examples/webxr-2/assets/audio/shellBounce.wav +0 -0
  148. package/examples/webxr-2/assets/audio/shellHit.wav +0 -0
  149. package/examples/webxr-2/assets/audio/shellKick.wav +0 -0
  150. package/examples/webxr-2/assets/audio/soccerBounce.wav +0 -0
  151. package/examples/webxr-2/assets/audio/soccerKick.mp3 +0 -0
  152. package/examples/webxr-2/assets/images/shellTexture.png +0 -0
  153. package/examples/webxr-2/components/bs-ankle.js +337 -0
  154. package/examples/webxr-2/components/coin.js +84 -0
  155. package/examples/webxr-2/components/custom-wrap.js +17 -0
  156. package/examples/webxr-2/components/goomba.js +3250 -0
  157. package/examples/webxr-2/components/init-shell-material.js +215 -0
  158. package/examples/webxr-2/components/platter.js +172 -0
  159. package/examples/webxr-2/components/shell.js +374 -0
  160. package/examples/webxr-2/components/soccer-ball.js +250 -0
  161. package/examples/webxr-2/components/squashed-goomba.js +249 -0
  162. package/examples/webxr-2/edge-impulse-standalone.js +7228 -0
  163. package/examples/webxr-2/edge-impulse-standalone.wasm +0 -0
  164. package/examples/webxr-2/index.html +996 -0
  165. package/examples/webxr-2/kick.tflite +0 -0
  166. package/examples/webxr-2/kick2.tflite +0 -0
  167. package/examples/webxr-2/run-impulse.js +135 -0
  168. package/examples/webxr-2/script.js +384 -0
  169. package/package.json +2 -1
  170. package/src/.prettierrc +4 -0
  171. package/src/BS.ts +66 -9
  172. package/src/CameraManager.ts +499 -0
  173. package/src/Device.ts +620 -92
  174. package/src/DeviceInformationManager.ts +22 -11
  175. package/src/DeviceManager.ts +94 -25
  176. package/src/FileTransferManager.ts +146 -21
  177. package/src/FirmwareManager.ts +1 -1
  178. package/src/InformationManager.ts +62 -20
  179. package/src/TfliteManager.ts +172 -26
  180. package/src/WifiManager.ts +323 -0
  181. package/src/connection/BaseConnectionManager.ts +145 -30
  182. package/src/connection/ClientConnectionManager.ts +47 -11
  183. package/src/connection/bluetooth/BluetoothConnectionManager.ts +14 -3
  184. package/src/connection/bluetooth/NobleConnectionManager.ts +155 -42
  185. package/src/connection/bluetooth/WebBluetoothConnectionManager.ts +104 -35
  186. package/src/connection/bluetooth/bluetoothUUIDs.ts +40 -13
  187. package/src/connection/udp/UDPConnectionManager.ts +356 -0
  188. package/src/connection/websocket/WebSocketConnectionManager.ts +282 -0
  189. package/src/devicePair/DevicePair.ts +145 -49
  190. package/src/devicePair/DevicePairPressureSensorDataManager.ts +72 -24
  191. package/src/devicePair/DevicePairSensorDataManager.ts +5 -5
  192. package/src/scanner/BaseScanner.ts +49 -11
  193. package/src/scanner/NobleScanner.ts +81 -17
  194. package/src/sensor/BarometerSensorDataManager.ts +1 -1
  195. package/src/sensor/MotionSensorDataManager.ts +22 -7
  196. package/src/sensor/PressureSensorDataManager.ts +47 -13
  197. package/src/sensor/SensorConfigurationManager.ts +75 -24
  198. package/src/sensor/SensorDataManager.ts +107 -26
  199. package/src/server/BaseClient.ts +192 -37
  200. package/src/server/BaseServer.ts +201 -43
  201. package/src/server/ServerUtils.ts +39 -9
  202. package/src/server/udp/UDPServer.ts +74 -23
  203. package/src/server/udp/UDPUtils.ts +9 -2
  204. package/src/server/websocket/WebSocketClient.ts +30 -9
  205. package/src/server/websocket/WebSocketServer.ts +1 -1
  206. package/src/server/websocket/WebSocketUtils.ts +4 -2
  207. package/src/utils/CenterOfPressureHelper.ts +5 -5
  208. package/src/utils/Console.ts +62 -9
  209. package/src/utils/MathUtils.ts +31 -1
  210. package/src/utils/ParseUtils.ts +25 -6
  211. package/src/utils/ThrottleUtils.ts +62 -0
  212. package/src/utils/Timer.ts +1 -1
  213. package/src/utils/checksum.ts +1 -1
  214. package/src/utils/mcumgr.js +1 -1
  215. package/src/vibration/VibrationManager.ts +166 -40
@@ -11,7 +11,9 @@ window.device = device;
11
11
 
12
12
  // GET DEVICES
13
13
  /** @type {HTMLTemplateElement} */
14
- const availableDeviceTemplate = document.getElementById("availableDeviceTemplate");
14
+ const availableDeviceTemplate = document.getElementById(
15
+ "availableDeviceTemplate"
16
+ );
15
17
  const availableDevicesContainer = document.getElementById("availableDevices");
16
18
  /** @param {BS.Device[]} availableDevices */
17
19
  function onAvailableDevices(availableDevices) {
@@ -23,19 +25,24 @@ function onAvailableDevices(availableDevices) {
23
25
  const availableDeviceContainer = availableDeviceTemplate.content
24
26
  .cloneNode(true)
25
27
  .querySelector(".availableDevice");
26
- availableDeviceContainer.querySelector(".name").innerText = availableDevice.name;
27
- availableDeviceContainer.querySelector(".type").innerText = availableDevice.type;
28
+ availableDeviceContainer.querySelector(".name").innerText =
29
+ availableDevice.name;
30
+ availableDeviceContainer.querySelector(".type").innerText =
31
+ availableDevice.type;
28
32
 
29
33
  /** @type {HTMLButtonElement} */
30
- const toggleConnectionButton = availableDeviceContainer.querySelector(".toggleConnection");
34
+ const toggleConnectionButton =
35
+ availableDeviceContainer.querySelector(".toggleConnection");
31
36
  toggleConnectionButton.addEventListener("click", () => {
32
37
  device.connectionManager = availableDevice.connectionManager;
33
38
  device.reconnect();
34
39
  });
35
40
  device.addEventListener("connectionStatus", () => {
36
- toggleConnectionButton.disabled = device.connectionStatus != "notConnected";
41
+ toggleConnectionButton.disabled =
42
+ device.connectionStatus != "notConnected";
37
43
  });
38
- toggleConnectionButton.disabled = device.connectionStatus != "notConnected";
44
+ toggleConnectionButton.disabled =
45
+ device.connectionStatus != "notConnected";
39
46
 
40
47
  availableDevicesContainer.appendChild(availableDeviceContainer);
41
48
  });
@@ -84,7 +91,9 @@ device.addEventListener("connectionStatus", () => {
84
91
  case "connected":
85
92
  case "notConnected":
86
93
  toggleConnectionButton.disabled = false;
87
- toggleConnectionButton.innerText = device.isConnected ? "disconnect" : "connect";
94
+ toggleConnectionButton.innerText = device.isConnected
95
+ ? "disconnect"
96
+ : "connect";
88
97
  break;
89
98
  case "connecting":
90
99
  case "disconnecting":
@@ -95,7 +104,29 @@ device.addEventListener("connectionStatus", () => {
95
104
  });
96
105
 
97
106
  /** @type {HTMLInputElement} */
98
- const reconnectOnDisconnectionCheckbox = document.getElementById("reconnectOnDisconnection");
107
+ const connectIpAddressInput = document.getElementById("connectIpAddress");
108
+ connectIpAddressInput.addEventListener("input", () => {
109
+ const isValid = connectIpAddressInput.checkValidity();
110
+ connectViaIpAddressButton.disabled = !isValid;
111
+ });
112
+ /** @type {HTMLButtonElement} */
113
+ const connectViaIpAddressButton = document.getElementById(
114
+ "connectViaIpAddress"
115
+ );
116
+ connectViaIpAddressButton.addEventListener("click", () => {
117
+ connectViaIpAddressButton.disabled = true;
118
+ console.log(`connecting via ipAddress "${connectIpAddressInput.value}"`);
119
+ device.connect({ type: "webSocket", ipAddress: connectIpAddressInput.value });
120
+ });
121
+ device.addEventListener("isConnected", () => {
122
+ connectIpAddressInput.disabled = device.isConnected;
123
+ connectViaIpAddressButton.disabled = device.isConnected;
124
+ });
125
+
126
+ /** @type {HTMLInputElement} */
127
+ const reconnectOnDisconnectionCheckbox = document.getElementById(
128
+ "reconnectOnDisconnection"
129
+ );
99
130
  reconnectOnDisconnectionCheckbox.addEventListener("input", () => {
100
131
  device.reconnectOnDisconnection = reconnectOnDisconnectionCheckbox.checked;
101
132
  });
@@ -103,7 +134,7 @@ reconnectOnDisconnectionCheckbox.addEventListener("input", () => {
103
134
  /** @type {HTMLButtonElement} */
104
135
  const resetDeviceButton = document.getElementById("resetDevice");
105
136
  device.addEventListener("isConnected", () => {
106
- resetDeviceButton.disabled = !device.isConnected;
137
+ resetDeviceButton.disabled = !device.isConnected || !device.canReset;
107
138
  });
108
139
  resetDeviceButton.addEventListener("click", () => {
109
140
  device.reset();
@@ -114,7 +145,17 @@ resetDeviceButton.addEventListener("click", () => {
114
145
  /** @type {HTMLPreElement} */
115
146
  const deviceInformationPre = document.getElementById("deviceInformationPre");
116
147
  device.addEventListener("deviceInformation", () => {
117
- deviceInformationPre.textContent = JSON.stringify(device.deviceInformation, null, 2);
148
+ deviceInformationPre.textContent = JSON.stringify(
149
+ device.deviceInformation,
150
+ null,
151
+ 2
152
+ );
153
+ });
154
+
155
+ // MTU
156
+ const deviceMtuSpan = document.getElementById("mtu");
157
+ device.addEventListener("getMtu", () => {
158
+ deviceMtuSpan.innerText = device.mtu;
118
159
  });
119
160
 
120
161
  // BATTERY LEVEL
@@ -141,7 +182,9 @@ device.addEventListener("getBatteryCurrent", () => {
141
182
  });
142
183
 
143
184
  /** @type {HTMLButtonElement} */
144
- const updateBatteryCurrentButton = document.getElementById("updateBatteryCurrent");
185
+ const updateBatteryCurrentButton = document.getElementById(
186
+ "updateBatteryCurrent"
187
+ );
145
188
  device.addEventListener("isConnected", () => {
146
189
  updateBatteryCurrentButton.disabled = !device.isConnected;
147
190
  });
@@ -225,22 +268,33 @@ setTypeButton.addEventListener("click", () => {
225
268
  // SENSOR CONFIGURATION
226
269
 
227
270
  /** @type {HTMLPreElement} */
228
- const sensorConfigurationPre = document.getElementById("sensorConfigurationPre");
271
+ const sensorConfigurationPre = document.getElementById(
272
+ "sensorConfigurationPre"
273
+ );
229
274
  device.addEventListener("getSensorConfiguration", () => {
230
- sensorConfigurationPre.textContent = JSON.stringify(device.sensorConfiguration, null, 2);
275
+ sensorConfigurationPre.textContent = JSON.stringify(
276
+ device.sensorConfiguration,
277
+ null,
278
+ 2
279
+ );
231
280
  });
232
281
 
233
282
  /** @type {HTMLTemplateElement} */
234
- const sensorTypeConfigurationTemplate = document.getElementById("sensorTypeConfigurationTemplate");
283
+ const sensorTypeConfigurationTemplate = document.getElementById(
284
+ "sensorTypeConfigurationTemplate"
285
+ );
235
286
  BS.SensorTypes.forEach((sensorType) => {
236
287
  /** @type {HTMLElement} */
237
- const sensorTypeConfigurationContainer = sensorTypeConfigurationTemplate.content
238
- .cloneNode(true)
239
- .querySelector(".sensorTypeConfiguration");
240
- sensorTypeConfigurationContainer.querySelector(".sensorType").innerText = sensorType;
288
+ const sensorTypeConfigurationContainer =
289
+ sensorTypeConfigurationTemplate.content
290
+ .cloneNode(true)
291
+ .querySelector(".sensorTypeConfiguration");
292
+ sensorTypeConfigurationContainer.querySelector(".sensorType").innerText =
293
+ sensorType;
241
294
 
242
295
  /** @type {HTMLInputElement} */
243
- const sensorRateInput = sensorTypeConfigurationContainer.querySelector(".sensorRate");
296
+ const sensorRateInput =
297
+ sensorTypeConfigurationContainer.querySelector(".sensorRate");
244
298
  sensorRateInput.value = 0;
245
299
  sensorRateInput.max = BS.MaxSensorRate;
246
300
  sensorRateInput.step = BS.SensorRateStep;
@@ -258,27 +312,44 @@ BS.SensorTypes.forEach((sensorType) => {
258
312
  }
259
313
  });
260
314
 
261
- sensorTypeConfigurationTemplate.parentElement.appendChild(sensorTypeConfigurationContainer);
315
+ sensorTypeConfigurationTemplate.parentElement.appendChild(
316
+ sensorTypeConfigurationContainer
317
+ );
262
318
  sensorTypeConfigurationContainer.dataset.sensorType = sensorType;
263
319
  });
264
320
  device.addEventListener("getSensorConfiguration", () => {
265
321
  for (const sensorType in device.sensorConfiguration) {
266
- document.querySelector(`.sensorTypeConfiguration[data-sensor-type="${sensorType}"] input`).value =
267
- device.sensorConfiguration[sensorType];
322
+ document.querySelector(
323
+ `.sensorTypeConfiguration[data-sensor-type="${sensorType}"] .input`
324
+ ).value = device.sensorConfiguration[sensorType];
268
325
  }
269
326
  });
270
327
  device.addEventListener("isConnected", () => {
271
328
  for (const sensorType in device.sensorConfiguration) {
272
- document.querySelector(`[data-sensor-type="${sensorType}"] input`).disabled = !device.isConnected;
329
+ document.querySelector(
330
+ `[data-sensor-type="${sensorType}"] .input`
331
+ ).disabled = !device.isConnected;
273
332
  }
274
333
  });
275
334
 
335
+ // PRESSURE RANGE
336
+
337
+ /** @type {HTMLButtonElement} */
338
+ const resetPressureRangeButton = document.getElementById("resetPressureRange");
339
+ resetPressureRangeButton.addEventListener("click", () => {
340
+ device.resetPressureRange();
341
+ });
342
+
276
343
  // SENSOR DATA
277
344
 
278
345
  /** @type {HTMLTemplateElement} */
279
- const sensorTypeDataTemplate = document.getElementById("sensorTypeDataTemplate");
346
+ const sensorTypeDataTemplate = document.getElementById(
347
+ "sensorTypeDataTemplate"
348
+ );
280
349
  BS.SensorTypes.forEach((sensorType) => {
281
- const sensorTypeDataContainer = sensorTypeDataTemplate.content.cloneNode(true).querySelector(".sensorTypeData");
350
+ const sensorTypeDataContainer = sensorTypeDataTemplate.content
351
+ .cloneNode(true)
352
+ .querySelector(".sensorTypeData");
282
353
  sensorTypeDataContainer.querySelector(".sensorType").innerText = sensorType;
283
354
 
284
355
  /** @type {HTMLPreElement} */
@@ -297,38 +368,52 @@ BS.SensorTypes.forEach((sensorType) => {
297
368
  const vibrationTemplate = document.getElementById("vibrationTemplate");
298
369
  {
299
370
  /** @type {HTMLInputElement} */
300
- const waveformEffectSequenceLoopCountInput = vibrationTemplate.content.querySelector(
301
- ".waveformEffect .sequenceLoopCount"
302
- );
303
- waveformEffectSequenceLoopCountInput.max = BS.MaxVibrationWaveformEffectSequenceLoopCount;
371
+ const waveformEffectSequenceLoopCountInput =
372
+ vibrationTemplate.content.querySelector(
373
+ ".waveformEffect .sequenceLoopCount"
374
+ );
375
+ waveformEffectSequenceLoopCountInput.max =
376
+ BS.MaxVibrationWaveformEffectSequenceLoopCount;
304
377
  }
305
378
  /** @type {HTMLTemplateElement} */
306
- const vibrationLocationTemplate = document.getElementById("vibrationLocationTemplate");
379
+ const vibrationLocationTemplate = document.getElementById(
380
+ "vibrationLocationTemplate"
381
+ );
307
382
 
308
383
  /** @type {HTMLTemplateElement} */
309
- const waveformEffectSegmentTemplate = document.getElementById("waveformEffectSegmentTemplate");
384
+ const waveformEffectSegmentTemplate = document.getElementById(
385
+ "waveformEffectSegmentTemplate"
386
+ );
310
387
  {
311
388
  /** @type {HTMLSelectElement} */
312
- const waveformEffectSelect = waveformEffectSegmentTemplate.content.querySelector(".effect");
389
+ const waveformEffectSelect =
390
+ waveformEffectSegmentTemplate.content.querySelector(".effect");
313
391
  const waveformEffectOptgroup = waveformEffectSelect.querySelector("optgroup");
314
392
  BS.VibrationWaveformEffects.forEach((waveformEffect) => {
315
393
  waveformEffectOptgroup.appendChild(new Option(waveformEffect));
316
394
  });
317
395
 
318
396
  /** @type {HTMLInputElement} */
319
- const waveformEffectSegmentDelayInput = waveformEffectSegmentTemplate.content.querySelector(".delay");
320
- waveformEffectSegmentDelayInput.max = BS.MaxVibrationWaveformEffectSegmentDelay;
397
+ const waveformEffectSegmentDelayInput =
398
+ waveformEffectSegmentTemplate.content.querySelector(".delay");
399
+ waveformEffectSegmentDelayInput.max =
400
+ BS.MaxVibrationWaveformEffectSegmentDelay;
321
401
 
322
402
  /** @type {HTMLInputElement} */
323
- const waveformEffectLoopCountInput = waveformEffectSegmentTemplate.content.querySelector(".loopCount");
324
- waveformEffectLoopCountInput.max = BS.MaxVibrationWaveformEffectSegmentLoopCount;
403
+ const waveformEffectLoopCountInput =
404
+ waveformEffectSegmentTemplate.content.querySelector(".loopCount");
405
+ waveformEffectLoopCountInput.max =
406
+ BS.MaxVibrationWaveformEffectSegmentLoopCount;
325
407
  }
326
408
 
327
409
  /** @type {HTMLTemplateElement} */
328
- const waveformSegmentTemplate = document.getElementById("waveformSegmentTemplate");
410
+ const waveformSegmentTemplate = document.getElementById(
411
+ "waveformSegmentTemplate"
412
+ );
329
413
  {
330
414
  /** @type {HTMLInputElement} */
331
- const waveformDurationSegmentInput = waveformSegmentTemplate.content.querySelector(".duration");
415
+ const waveformDurationSegmentInput =
416
+ waveformSegmentTemplate.content.querySelector(".duration");
332
417
  waveformDurationSegmentInput.max = BS.MaxVibrationWaveformSegmentDuration;
333
418
  }
334
419
 
@@ -336,7 +421,9 @@ const waveformSegmentTemplate = document.getElementById("waveformSegmentTemplate
336
421
  const addVibrationButton = document.getElementById("addVibration");
337
422
  addVibrationButton.addEventListener("click", () => {
338
423
  /** @type {HTMLElement} */
339
- const vibrationContainer = vibrationTemplate.content.cloneNode(true).querySelector(".vibration");
424
+ const vibrationContainer = vibrationTemplate.content
425
+ .cloneNode(true)
426
+ .querySelector(".vibration");
340
427
 
341
428
  /** @type {HTMLButtonElement} */
342
429
  const deleteButton = vibrationContainer.querySelector(".delete");
@@ -346,25 +433,33 @@ addVibrationButton.addEventListener("click", () => {
346
433
  });
347
434
 
348
435
  /** @type {HTMLUListElement} */
349
- const vibrationLocationsContainer = vibrationContainer.querySelector(".locations");
350
- BS.VibrationLocations.forEach((vibrationLocation) => {
436
+ const vibrationLocationsContainer =
437
+ vibrationContainer.querySelector(".locations");
438
+ device.vibrationLocations.forEach((vibrationLocation) => {
351
439
  const vibrationLocationContainer = vibrationLocationTemplate.content
352
440
  .cloneNode(true)
353
441
  .querySelector(".vibrationLocation");
354
- vibrationLocationContainer.querySelector("span").innerText = vibrationLocation;
355
- vibrationLocationContainer.querySelector("input").dataset.vibrationLocation = vibrationLocation;
442
+ vibrationLocationContainer.querySelector("span").innerText =
443
+ vibrationLocation;
444
+ vibrationLocationContainer.querySelector(
445
+ "input"
446
+ ).dataset.vibrationLocation = vibrationLocation;
356
447
  vibrationLocationsContainer.appendChild(vibrationLocationContainer);
357
448
  });
358
449
 
359
450
  /** @type {HTMLElement} */
360
- const waveformEffectContainer = vibrationContainer.querySelector(".waveformEffect");
451
+ const waveformEffectContainer =
452
+ vibrationContainer.querySelector(".waveformEffect");
361
453
  /** @type {HTMLUListElement} */
362
- const waveformEffectSegmentsContainer = waveformEffectContainer.querySelector(".segments");
454
+ const waveformEffectSegmentsContainer =
455
+ waveformEffectContainer.querySelector(".segments");
363
456
  /** @type {HTMLButtonElement} */
364
- const addWaveformEffectSegmentButton = waveformEffectContainer.querySelector(".add");
457
+ const addWaveformEffectSegmentButton =
458
+ waveformEffectContainer.querySelector(".add");
365
459
  const updateAddWaveformEffectSegmentButton = () => {
366
460
  addWaveformEffectSegmentButton.disabled =
367
- waveformEffectSegmentsContainer.children.length >= BS.MaxNumberOfVibrationWaveformEffectSegments;
461
+ waveformEffectSegmentsContainer.children.length >=
462
+ BS.MaxNumberOfVibrationWaveformEffectSegments;
368
463
  };
369
464
  addWaveformEffectSegmentButton.addEventListener("click", () => {
370
465
  /** @type {HTMLElement} */
@@ -372,11 +467,14 @@ addVibrationButton.addEventListener("click", () => {
372
467
  .cloneNode(true)
373
468
  .querySelector(".waveformEffectSegment");
374
469
 
375
- const effectContainer = waveformEffectSegmentContainer.querySelector(".effect").parentElement;
376
- const delayContainer = waveformEffectSegmentContainer.querySelector(".delay").parentElement;
470
+ const effectContainer =
471
+ waveformEffectSegmentContainer.querySelector(".effect").parentElement;
472
+ const delayContainer =
473
+ waveformEffectSegmentContainer.querySelector(".delay").parentElement;
377
474
 
378
475
  /** @type {HTMLSelectElement} */
379
- const waveformEffectTypeSelect = waveformEffectSegmentContainer.querySelector(".type");
476
+ const waveformEffectTypeSelect =
477
+ waveformEffectSegmentContainer.querySelector(".type");
380
478
  waveformEffectTypeSelect.addEventListener("input", () => {
381
479
  let shouldShowEffectContainer = false;
382
480
  let shouldShowDelayContainer = false;
@@ -389,7 +487,9 @@ addVibrationButton.addEventListener("click", () => {
389
487
  shouldShowDelayContainer = true;
390
488
  break;
391
489
  default:
392
- throw Error(`uncaught waveformEffectTypeSelect value "${waveformEffectTypeSelect.value}"`);
490
+ throw Error(
491
+ `uncaught waveformEffectTypeSelect value "${waveformEffectTypeSelect.value}"`
492
+ );
393
493
  }
394
494
 
395
495
  effectContainer.style.display = shouldShowEffectContainer ? "" : "none";
@@ -397,10 +497,12 @@ addVibrationButton.addEventListener("click", () => {
397
497
  });
398
498
  waveformEffectTypeSelect.dispatchEvent(new Event("input"));
399
499
 
400
- waveformEffectSegmentContainer.querySelector(".delete").addEventListener("click", () => {
401
- waveformEffectSegmentContainer.remove();
402
- updateAddWaveformEffectSegmentButton();
403
- });
500
+ waveformEffectSegmentContainer
501
+ .querySelector(".delete")
502
+ .addEventListener("click", () => {
503
+ waveformEffectSegmentContainer.remove();
504
+ updateAddWaveformEffectSegmentButton();
505
+ });
404
506
 
405
507
  waveformEffectSegmentsContainer.appendChild(waveformEffectSegmentContainer);
406
508
  updateAddWaveformEffectSegmentButton();
@@ -409,22 +511,28 @@ addVibrationButton.addEventListener("click", () => {
409
511
  /** @type {HTMLElement} */
410
512
  const waveformContainer = vibrationContainer.querySelector(".waveform");
411
513
  /** @type {HTMLUListElement} */
412
- const waveformSegmentsContainer = waveformContainer.querySelector(".segments");
514
+ const waveformSegmentsContainer =
515
+ waveformContainer.querySelector(".segments");
413
516
 
414
517
  /** @type {HTMLButtonElement} */
415
518
  const addWaveformSegmentButton = waveformContainer.querySelector(".add");
416
519
  const updateAddWaveformSegmentButton = () => {
417
520
  addWaveformSegmentButton.disabled =
418
- waveformSegmentsContainer.children.length >= BS.MaxNumberOfVibrationWaveformSegments;
521
+ waveformSegmentsContainer.children.length >=
522
+ BS.MaxNumberOfVibrationWaveformSegments;
419
523
  };
420
524
  addWaveformSegmentButton.addEventListener("click", () => {
421
525
  /** @type {HTMLElement} */
422
- const waveformSegmentContainer = waveformSegmentTemplate.content.cloneNode(true).querySelector(".waveformSegment");
526
+ const waveformSegmentContainer = waveformSegmentTemplate.content
527
+ .cloneNode(true)
528
+ .querySelector(".waveformSegment");
423
529
 
424
- waveformSegmentContainer.querySelector(".delete").addEventListener("click", () => {
425
- waveformSegmentContainer.remove();
426
- updateAddWaveformSegmentButton();
427
- });
530
+ waveformSegmentContainer
531
+ .querySelector(".delete")
532
+ .addEventListener("click", () => {
533
+ waveformSegmentContainer.remove();
534
+ updateAddWaveformSegmentButton();
535
+ });
428
536
 
429
537
  waveformSegmentsContainer.appendChild(waveformSegmentContainer);
430
538
  updateAddWaveformSegmentButton();
@@ -433,7 +541,8 @@ addVibrationButton.addEventListener("click", () => {
433
541
  /** @type {HTMLSelectElement} */
434
542
  const vibrationTypeSelect = vibrationContainer.querySelector(".type");
435
543
  /** @type {HTMLOptGroupElement} */
436
- const vibrationTypeSelectOptgroup = vibrationTypeSelect.querySelector("optgroup");
544
+ const vibrationTypeSelectOptgroup =
545
+ vibrationTypeSelect.querySelector("optgroup");
437
546
  BS.VibrationTypes.forEach((vibrationType) => {
438
547
  vibrationTypeSelectOptgroup.appendChild(new Option(vibrationType));
439
548
  });
@@ -455,7 +564,9 @@ addVibrationButton.addEventListener("click", () => {
455
564
  throw Error(`invalid vibrationType "${vibrationType}"`);
456
565
  }
457
566
 
458
- waveformEffectContainer.style.display = showWaveformEffectContainer ? "" : "none";
567
+ waveformEffectContainer.style.display = showWaveformEffectContainer
568
+ ? ""
569
+ : "none";
459
570
  waveformContainer.style.display = showWaveformContainer ? "" : "none";
460
571
  });
461
572
  vibrationTypeSelect.dispatchEvent(new Event("input"));
@@ -470,40 +581,60 @@ triggerVibrationsButton.addEventListener("click", () => {
470
581
  /** @type {BS.VibrationConfiguration[]} */
471
582
  let vibrationConfigurations = [];
472
583
  Array.from(vibrationTemplate.parentElement.querySelectorAll(".vibration"))
473
- .filter((vibrationContainer) => vibrationContainer.querySelector(".shouldTrigger").checked)
584
+ .filter(
585
+ (vibrationContainer) =>
586
+ vibrationContainer.querySelector(".shouldTrigger").checked
587
+ )
474
588
  .forEach((vibrationContainer) => {
475
589
  /** @type {BS.VibrationConfiguration} */
476
590
  const vibrationConfiguration = {
477
591
  locations: [],
478
592
  };
479
- Array.from(vibrationContainer.querySelectorAll(`[data-vibration-location]`))
593
+ Array.from(
594
+ vibrationContainer.querySelectorAll(`[data-vibration-location]`)
595
+ )
480
596
  .filter((input) => input.checked)
481
597
  .forEach((input) => {
482
- vibrationConfiguration.locations.push(input.dataset.vibrationLocation);
598
+ vibrationConfiguration.locations.push(
599
+ input.dataset.vibrationLocation
600
+ );
483
601
  });
484
602
  if (vibrationConfiguration.locations.length == 0) {
485
603
  return;
486
604
  }
487
605
 
488
- vibrationConfiguration.type = vibrationContainer.querySelector("select.type").value;
606
+ vibrationConfiguration.type =
607
+ vibrationContainer.querySelector("select.type").value;
489
608
  switch (vibrationConfiguration.type) {
490
609
  case "waveformEffect":
491
610
  vibrationConfiguration.segments = Array.from(
492
- vibrationContainer.querySelectorAll(".waveformEffect .waveformEffectSegment")
611
+ vibrationContainer.querySelectorAll(
612
+ ".waveformEffect .waveformEffectSegment"
613
+ )
493
614
  ).map((waveformEffectSegmentContainer) => {
494
615
  /** @type {BS.VibrationWaveformEffectSegment} */
495
616
  const waveformEffectSegment = {
496
- loopCount: Number(waveformEffectSegmentContainer.querySelector(".loopCount").value),
617
+ loopCount: Number(
618
+ waveformEffectSegmentContainer.querySelector(".loopCount").value
619
+ ),
497
620
  };
498
- if (waveformEffectSegmentContainer.querySelector(".type").value == "effect") {
499
- waveformEffectSegment.effect = waveformEffectSegmentContainer.querySelector(".effect").value;
621
+ if (
622
+ waveformEffectSegmentContainer.querySelector(".type").value ==
623
+ "effect"
624
+ ) {
625
+ waveformEffectSegment.effect =
626
+ waveformEffectSegmentContainer.querySelector(".effect").value;
500
627
  } else {
501
- waveformEffectSegment.delay = Number(waveformEffectSegmentContainer.querySelector(".delay").value);
628
+ waveformEffectSegment.delay = Number(
629
+ waveformEffectSegmentContainer.querySelector(".delay").value
630
+ );
502
631
  }
503
632
  return waveformEffectSegment;
504
633
  });
505
634
  vibrationConfiguration.loopCount = Number(
506
- vibrationContainer.querySelector(".waveformEffect .sequenceLoopCount").value
635
+ vibrationContainer.querySelector(
636
+ ".waveformEffect .sequenceLoopCount"
637
+ ).value
507
638
  );
508
639
  break;
509
640
  case "waveform":
@@ -511,8 +642,12 @@ triggerVibrationsButton.addEventListener("click", () => {
511
642
  vibrationContainer.querySelectorAll(".waveform .waveformSegment")
512
643
  ).map((waveformSegmentContainer) => {
513
644
  return {
514
- amplitude: Number(waveformSegmentContainer.querySelector(".amplitude").value),
515
- duration: Number(waveformSegmentContainer.querySelector(".duration").value),
645
+ amplitude: Number(
646
+ waveformSegmentContainer.querySelector(".amplitude").value
647
+ ),
648
+ duration: Number(
649
+ waveformSegmentContainer.querySelector(".duration").value
650
+ ),
516
651
  };
517
652
  });
518
653
  break;
@@ -532,7 +667,8 @@ device.addEventListener("isConnected", () => {
532
667
 
533
668
  function updateTriggerVibrationsButtonDisabled() {
534
669
  triggerVibrationsButton.disabled =
535
- !device.isConnected || vibrationTemplate.parentElement.querySelectorAll(".vibration").length == 0;
670
+ !device.isConnected ||
671
+ vibrationTemplate.parentElement.querySelectorAll(".vibration").length == 0;
536
672
  }
537
673
 
538
674
  // FILE TRANSFER
@@ -573,14 +709,28 @@ fileTransferTypesSelect.addEventListener("input", () => {
573
709
  case "tflite":
574
710
  fileInput.accept = ".tflite";
575
711
  break;
712
+ case "wifiServerCert":
713
+ fileInput.accept = ".crt";
714
+ break;
715
+ case "wifiServerKey":
716
+ fileInput.accept = ".key";
717
+ break;
576
718
  }
577
719
  });
578
720
  /** @type {HTMLOptGroupElement} */
579
- const fileTransferTypesOptgroup = fileTransferTypesSelect.querySelector("optgroup");
721
+ const fileTransferTypesOptgroup =
722
+ fileTransferTypesSelect.querySelector("optgroup");
580
723
  BS.FileTypes.forEach((fileType) => {
581
724
  fileTransferTypesOptgroup.appendChild(new Option(fileType));
582
725
  });
583
726
  fileTransferTypesSelect.dispatchEvent(new Event("input"));
727
+ device.addEventListener("connected", () => {
728
+ fileTransferTypesSelect.querySelectorAll("option").forEach((option) => {
729
+ option.hidden =
730
+ BS.FileTypes.includes(option.value) &&
731
+ !device.fileTypes.includes(option.value);
732
+ });
733
+ });
584
734
 
585
735
  /** @type {HTMLProgressElement} */
586
736
  const fileTransferProgress = document.getElementById("fileTransferProgress");
@@ -613,7 +763,8 @@ toggleFileTransferButton.addEventListener("click", async () => {
613
763
  }
614
764
  });
615
765
  const updateToggleFileTransferButton = () => {
616
- const enabled = device.isConnected && (file || fileTransferDirection == "receive");
766
+ const enabled =
767
+ device.isConnected && (file || fileTransferDirection == "receive");
617
768
  toggleFileTransferButton.disabled = !enabled;
618
769
 
619
770
  /** @type {String} */
@@ -641,7 +792,9 @@ device.addEventListener("fileTransferStatus", () => {
641
792
  /** @type {BS.FileTransferDirection} */
642
793
  let fileTransferDirection;
643
794
  /** @type {HTMLSelectElement} */
644
- const fileTransferDirectionSelect = document.getElementById("fileTransferDirection");
795
+ const fileTransferDirectionSelect = document.getElementById(
796
+ "fileTransferDirection"
797
+ );
645
798
  fileTransferDirectionSelect.addEventListener("input", () => {
646
799
  fileTransferDirection = fileTransferDirectionSelect.value;
647
800
  console.log({ fileTransferDirection });
@@ -745,9 +898,13 @@ device.addEventListener("getTfliteInferencingEnabled", () => {
745
898
  /** @type {HTMLSpanElement} */
746
899
  const tfliteSampleRateSpan = document.getElementById("tfliteSampleRate");
747
900
  /** @type {HTMLInputElement} */
748
- const setTfliteSampleRateInput = document.getElementById("setTfliteSampleRateInput");
901
+ const setTfliteSampleRateInput = document.getElementById(
902
+ "setTfliteSampleRateInput"
903
+ );
749
904
  /** @type {HTMLButtonElement} */
750
- const setTfliteSampleRateButton = document.getElementById("setTfliteSampleRateButton");
905
+ const setTfliteSampleRateButton = document.getElementById(
906
+ "setTfliteSampleRateButton"
907
+ );
751
908
 
752
909
  device.addEventListener("isConnected", () => {
753
910
  const disabled = !device.isConnected;
@@ -779,16 +936,22 @@ device.addEventListener("getTfliteInferencingEnabled", () => {
779
936
 
780
937
  const tfliteSensorTypesContainer = document.getElementById("tfliteSensorTypes");
781
938
  /** @type {HTMLTemplateElement} */
782
- const tfliteSensorTypeTemplate = document.getElementById("tfliteSensorTypeTemplate");
939
+ const tfliteSensorTypeTemplate = document.getElementById(
940
+ "tfliteSensorTypeTemplate"
941
+ );
783
942
  /** @type {Object.<string, HTMLElement>} */
784
943
  const tfliteSensorTypeContainers = {};
785
944
  /** @type {BS.SensorType[]} */
786
945
  let tfliteSensorTypes = [];
787
946
  /** @type {HTMLButtonElement} */
788
- const setTfliteSensorTypesButton = document.getElementById("setTfliteSensorTypes");
947
+ const setTfliteSensorTypesButton = document.getElementById(
948
+ "setTfliteSensorTypes"
949
+ );
789
950
 
790
951
  BS.TfliteSensorTypes.forEach((sensorType) => {
791
- const sensorTypeContainer = tfliteSensorTypeTemplate.content.cloneNode(true).querySelector(".sensorType");
952
+ const sensorTypeContainer = tfliteSensorTypeTemplate.content
953
+ .cloneNode(true)
954
+ .querySelector(".sensorType");
792
955
  sensorTypeContainer.querySelector(".name").innerText = sensorType;
793
956
 
794
957
  /** @type {HTMLInputElement} */
@@ -803,7 +966,8 @@ BS.TfliteSensorTypes.forEach((sensorType) => {
803
966
  });
804
967
 
805
968
  device.addEventListener("getTfliteSensorTypes", () => {
806
- isSensorEnabledInput.checked = device.tfliteSensorTypes.includes(sensorType);
969
+ isSensorEnabledInput.checked =
970
+ device.tfliteSensorTypes.includes(sensorType);
807
971
  });
808
972
  isSensorEnabledInput.checked = device.tfliteSensorTypes.includes(sensorType);
809
973
 
@@ -838,9 +1002,13 @@ device.addEventListener("tfliteIsReady", () => {
838
1002
  /** @type {HTMLSpanElement} */
839
1003
  const tfliteThresholdSpan = document.getElementById("tfliteThreshold");
840
1004
  /** @type {HTMLInputElement} */
841
- const setTfliteThresholdInput = document.getElementById("setTfliteThresholdInput");
1005
+ const setTfliteThresholdInput = document.getElementById(
1006
+ "setTfliteThresholdInput"
1007
+ );
842
1008
  /** @type {HTMLButtonElement} */
843
- const setTfliteThresholdButton = document.getElementById("setTfliteThresholdButton");
1009
+ const setTfliteThresholdButton = document.getElementById(
1010
+ "setTfliteThresholdButton"
1011
+ );
844
1012
 
845
1013
  device.addEventListener("isConnected", () => {
846
1014
  const disabled = !device.isConnected;
@@ -870,9 +1038,13 @@ setTfliteThresholdButton.addEventListener("click", () => {
870
1038
  /** @type {HTMLSpanElement} */
871
1039
  const tfliteCaptureDelaySpan = document.getElementById("tfliteCaptureDelay");
872
1040
  /** @type {HTMLInputElement} */
873
- const setTfliteCaptureDelayInput = document.getElementById("setTfliteCaptureDelayInput");
1041
+ const setTfliteCaptureDelayInput = document.getElementById(
1042
+ "setTfliteCaptureDelayInput"
1043
+ );
874
1044
  /** @type {HTMLButtonElement} */
875
- const setTfliteCaptureDelayButton = document.getElementById("setTfliteCaptureDelayButton");
1045
+ const setTfliteCaptureDelayButton = document.getElementById(
1046
+ "setTfliteCaptureDelayButton"
1047
+ );
876
1048
 
877
1049
  device.addEventListener("isConnected", () => {
878
1050
  const disabled = !device.isConnected;
@@ -900,18 +1072,23 @@ setTfliteCaptureDelayButton.addEventListener("click", () => {
900
1072
  });
901
1073
 
902
1074
  /** @type {HTMLInputElement} */
903
- const tfliteInferencingEnabledInput = document.getElementById("tfliteInferencingEnabled");
1075
+ const tfliteInferencingEnabledInput = document.getElementById(
1076
+ "tfliteInferencingEnabled"
1077
+ );
904
1078
  /** @type {HTMLButtonElement} */
905
- const toggleTfliteInferencingEnabledButton = document.getElementById("toggleTfliteInferencingEnabled");
1079
+ const toggleTfliteInferencingEnabledButton = document.getElementById(
1080
+ "toggleTfliteInferencingEnabled"
1081
+ );
906
1082
 
907
1083
  device.addEventListener("tfliteIsReady", () => {
908
1084
  toggleTfliteInferencingEnabledButton.disabled = !device.tfliteIsReady;
909
1085
  });
910
1086
  device.addEventListener("getTfliteInferencingEnabled", () => {
911
1087
  tfliteInferencingEnabledInput.checked = device.tfliteInferencingEnabled;
912
- toggleTfliteInferencingEnabledButton.innerText = device.tfliteInferencingEnabled
913
- ? "disable inferencing"
914
- : "enable inferencing";
1088
+ toggleTfliteInferencingEnabledButton.innerText =
1089
+ device.tfliteInferencingEnabled
1090
+ ? "disable inferencing"
1091
+ : "enable inferencing";
915
1092
  });
916
1093
 
917
1094
  toggleTfliteInferencingEnabledButton.addEventListener("click", () => {
@@ -944,7 +1121,8 @@ device.addEventListener("tfliteInference", (event) => {
944
1121
  tfliteInferencePre.textContent = JSON.stringify(tfliteInference, null, 2);
945
1122
 
946
1123
  if (device.tfliteTask == "classification") {
947
- topInferenceClassElement.innerText = inferenceClasses[tfliteInference.maxIndex - 1] ?? "";
1124
+ topInferenceClassElement.innerText =
1125
+ inferenceClasses[tfliteInference.maxIndex - 1] ?? "";
948
1126
  }
949
1127
  });
950
1128
 
@@ -960,7 +1138,9 @@ firmwareInput.addEventListener("input", () => {
960
1138
  updateToggleFirmwareUploadButton();
961
1139
  });
962
1140
  /** @type {HTMLButtonElement} */
963
- const toggleFirmwareUploadButton = document.getElementById("toggleFirmwareUpload");
1141
+ const toggleFirmwareUploadButton = document.getElementById(
1142
+ "toggleFirmwareUpload"
1143
+ );
964
1144
  toggleFirmwareUploadButton.addEventListener("click", () => {
965
1145
  device.uploadFirmware(firmware);
966
1146
  });
@@ -973,20 +1153,28 @@ device.addEventListener("isConnected", () => {
973
1153
  });
974
1154
 
975
1155
  /** @type {HTMLProgressElement} */
976
- const firmwareUploadProgress = document.getElementById("firmwareUploadProgress");
1156
+ const firmwareUploadProgress = document.getElementById(
1157
+ "firmwareUploadProgress"
1158
+ );
977
1159
  /** @type {HTMLSpanElement} */
978
- const firmwareUploadProgressPercentageSpan = document.getElementById("firmwareUploadProgressPercentage");
1160
+ const firmwareUploadProgressPercentageSpan = document.getElementById(
1161
+ "firmwareUploadProgressPercentage"
1162
+ );
979
1163
  device.addEventListener("firmwareUploadProgress", (event) => {
980
1164
  const progress = event.message.progress;
981
1165
  firmwareUploadProgress.value = progress;
982
- firmwareUploadProgressPercentageSpan.innerText = `${Math.floor(100 * progress)}%`;
1166
+ firmwareUploadProgressPercentageSpan.innerText = `${Math.floor(
1167
+ 100 * progress
1168
+ )}%`;
983
1169
  });
984
1170
  device.addEventListener("firmwareUploadComplete", () => {
985
1171
  firmwareUploadProgress.value = 0;
986
1172
  });
987
1173
  device.addEventListener("firmwareStatus", () => {
988
1174
  const isUploading = device.firmwareStatus == "uploading";
989
- firmwareUploadProgressPercentageSpan.style.display = isUploading ? "" : "none";
1175
+ firmwareUploadProgressPercentageSpan.style.display = isUploading
1176
+ ? ""
1177
+ : "none";
990
1178
  });
991
1179
 
992
1180
  /** @type {HTMLPreElement} */
@@ -1000,7 +1188,7 @@ device.addEventListener("firmwareImages", () => {
1000
1188
  });
1001
1189
 
1002
1190
  device.addEventListener("isConnected", () => {
1003
- if (device.isConnected) {
1191
+ if (device.isConnected && device.canUpdateFirmware) {
1004
1192
  device.getFirmwareImages();
1005
1193
  }
1006
1194
  });
@@ -1026,7 +1214,7 @@ resetButton.addEventListener("click", () => {
1026
1214
  const updateResetButton = () => {
1027
1215
  const status = device.firmwareStatus;
1028
1216
  const enabled = status == "pending" || status == "testing";
1029
- resetButton.disabled = !enabled;
1217
+ resetButton.disabled = !enabled || !device.canReset;
1030
1218
  };
1031
1219
 
1032
1220
  /** @type {HTMLButtonElement} */
@@ -1040,12 +1228,15 @@ const updateTestFirmwareImageButton = () => {
1040
1228
  };
1041
1229
 
1042
1230
  /** @type {HTMLButtonElement} */
1043
- const confirmFirmwareImageButton = document.getElementById("confirmFirmwareImage");
1231
+ const confirmFirmwareImageButton = document.getElementById(
1232
+ "confirmFirmwareImage"
1233
+ );
1044
1234
  confirmFirmwareImageButton.addEventListener("click", () => {
1045
1235
  device.confirmFirmwareImage(selectedImageIndex);
1046
1236
  });
1047
1237
  const updateConfirmFirmwareImageButton = () => {
1048
- const enabled = device.firmwareStatus == "testing" || device.firmwareStatus == "uploaded";
1238
+ const enabled =
1239
+ device.firmwareStatus == "testing" || device.firmwareStatus == "uploaded";
1049
1240
  confirmFirmwareImageButton.disabled = !enabled;
1050
1241
  };
1051
1242
 
@@ -1066,7 +1257,10 @@ const imageSelectionOptGroup = imageSelectionSelect.querySelector("optgroup");
1066
1257
  device.addEventListener("firmwareImages", () => {
1067
1258
  imageSelectionOptGroup.innerHTML = "";
1068
1259
  device.firmwareImages.forEach((firmwareImage, index) => {
1069
- const option = new Option(`${firmwareImage.version} (slot ${index})`, index);
1260
+ const option = new Option(
1261
+ `${firmwareImage.version} (slot ${index})`,
1262
+ index
1263
+ );
1070
1264
  option.disabled = firmwareImage.empty;
1071
1265
  imageSelectionOptGroup.appendChild(option);
1072
1266
  });
@@ -1091,3 +1285,449 @@ function updateSelectImageSelect() {
1091
1285
  }
1092
1286
  imageSelectionSelect.disabled = !enabled;
1093
1287
  }
1288
+
1289
+ // WIFI
1290
+
1291
+ /** @type {HTMLSpanElement} */
1292
+ const isWifiAvailableSpan = document.getElementById("isWifiAvailable");
1293
+ device.addEventListener("isWifiAvailable", (event) => {
1294
+ isWifiAvailableSpan.innerText = event.message.isWifiAvailable;
1295
+ });
1296
+
1297
+ /** @type {HTMLSpanElement} */
1298
+ const wifiSSIDSpan = document.getElementById("wifiSSID");
1299
+ device.addEventListener("getWifiSSID", (event) => {
1300
+ wifiSSIDSpan.innerText = event.message.wifiSSID;
1301
+ });
1302
+ /** @type {HTMLInputElement} */
1303
+ const setWifiSSIDInput = document.getElementById("setWifiSSIDInput");
1304
+ setWifiSSIDInput.min = BS.MinWifiSSIDLength;
1305
+ setWifiSSIDInput.max = BS.MaxWifiSSIDLength;
1306
+ setWifiSSIDInput.addEventListener("input", () => {
1307
+ setWifiSSIDButton.disabled = !(
1308
+ setWifiSSIDInput.value.length > BS.MinWifiSSIDLength &&
1309
+ setWifiSSIDInput.value.length < BS.MaxWifiSSIDLength
1310
+ );
1311
+ });
1312
+ /** @type {HTMLButtonElement} */
1313
+ const setWifiSSIDButton = document.getElementById("setWifiSSIDButton");
1314
+ setWifiSSIDButton.addEventListener("click", async () => {
1315
+ setWifiSSIDButton.disabled = true;
1316
+ setWifiSSIDButton.innerText = "setting wifi ssid";
1317
+ await device.setWifiSSID(setWifiSSIDInput.value);
1318
+ setWifiSSIDInput.value = "";
1319
+ setWifiSSIDButton.disabled = false;
1320
+ setWifiSSIDButton.innerText = "set wifi ssid";
1321
+ });
1322
+
1323
+ /** @type {HTMLSpanElement} */
1324
+ const wifiPasswordSpan = document.getElementById("wifiPassword");
1325
+ device.addEventListener("getWifiPassword", (event) => {
1326
+ wifiPasswordSpan.innerText = event.message.wifiPassword;
1327
+ });
1328
+ /** @type {HTMLInputElement} */
1329
+ const setWifiPasswordInput = document.getElementById("setWifiPasswordInput");
1330
+ setWifiPasswordInput.min = BS.MinWifiPasswordLength;
1331
+ setWifiPasswordInput.max = BS.MaxWifiPasswordLength;
1332
+ setWifiPasswordInput.addEventListener("input", () => {
1333
+ const length = setWifiPasswordInput.value.length;
1334
+ setWifiPasswordButton.disabled = !(
1335
+ length == 0 ||
1336
+ (length > BS.MinWifiPasswordLength && length < BS.MaxWifiPasswordLength)
1337
+ );
1338
+ });
1339
+ /** @type {HTMLButtonElement} */
1340
+ const setWifiPasswordButton = document.getElementById("setWifiPasswordButton");
1341
+ setWifiPasswordButton.addEventListener("click", async () => {
1342
+ setWifiPasswordButton.disabled = true;
1343
+ setWifiPasswordButton.innerText = "setting wifi password";
1344
+ await device.setWifiPassword(setWifiPasswordInput.value);
1345
+ setWifiPasswordInput.value = "";
1346
+ setWifiPasswordButton.disabled = false;
1347
+ setWifiPasswordButton.innerText = "set wifi password";
1348
+ });
1349
+
1350
+ const updateWifiInputs = () => {
1351
+ const enabled =
1352
+ device.isConnected &&
1353
+ device.isWifiAvailable &&
1354
+ !device.wifiConnectionEnabled;
1355
+ const disabled = !enabled;
1356
+
1357
+ setWifiPasswordInput.disabled = disabled;
1358
+ setWifiPasswordButton.disabled = disabled;
1359
+
1360
+ setWifiSSIDInput.disabled = disabled;
1361
+ setWifiSSIDButton.disabled = disabled;
1362
+
1363
+ toggleWifiConnectionButton.disabled = !(
1364
+ device.isConnected && device.isWifiAvailable
1365
+ );
1366
+ };
1367
+ device.addEventListener("isConnected", () => {
1368
+ updateWifiInputs();
1369
+ });
1370
+ device.addEventListener("getWifiConnectionEnabled", () => {
1371
+ updateWifiInputs();
1372
+ });
1373
+
1374
+ /** @type {HTMLSpanElement} */
1375
+ const wifiConnectionEnabledSpan = document.getElementById(
1376
+ "wifiConnectionEnabled"
1377
+ );
1378
+ device.addEventListener("getWifiConnectionEnabled", (event) => {
1379
+ wifiConnectionEnabledSpan.innerText = event.message.wifiConnectionEnabled;
1380
+ });
1381
+
1382
+ /** @type {HTMLSpanElement} */
1383
+ const isWifiConnectedSpan = document.getElementById("isWifiConnected");
1384
+ device.addEventListener("isWifiConnected", (event) => {
1385
+ isWifiConnectedSpan.innerText = event.message.isWifiConnected;
1386
+ });
1387
+
1388
+ /** @type {HTMLSpanElement} */
1389
+ const ipAddressSpan = document.getElementById("ipAddress");
1390
+ device.addEventListener("ipAddress", (event) => {
1391
+ ipAddressSpan.innerText = event.message.ipAddress || "none";
1392
+ });
1393
+
1394
+ /** @type {HTMLButtonElement} */
1395
+ const toggleWifiConnectionButton = document.getElementById(
1396
+ "toggleWifiConnection"
1397
+ );
1398
+ toggleWifiConnectionButton.addEventListener("click", async () => {
1399
+ toggleWifiConnectionButton.disabled = true;
1400
+ await device.toggleWifiConnection();
1401
+ toggleWifiConnectionButton.disabled = false;
1402
+ });
1403
+ device.addEventListener("getWifiConnectionEnabled", (event) => {
1404
+ toggleWifiConnectionButton.innerText = event.message.wifiConnectionEnabled
1405
+ ? "disable wifi connection"
1406
+ : "enable wifi connection";
1407
+ });
1408
+
1409
+ /** @type {HTMLButtonElement} */
1410
+ const connectViaWebSocketsButton = document.getElementById(
1411
+ "connectViaWebSockets"
1412
+ );
1413
+ connectViaWebSocketsButton.addEventListener("click", async () => {
1414
+ toggleWifiConnectionButton.disabled = true;
1415
+ device.reconnectViaWebSockets();
1416
+ });
1417
+ const updateConnectViaWebSocketsButton = () => {
1418
+ const enabled =
1419
+ device.isConnected &&
1420
+ device.connectionType == "webBluetooth" &&
1421
+ device.isWifiConnected;
1422
+ console.log({ enabled });
1423
+ connectViaWebSocketsButton.disabled = !enabled;
1424
+ };
1425
+ device.addEventListener("isWifiConnected", () =>
1426
+ updateConnectViaWebSocketsButton()
1427
+ );
1428
+ device.addEventListener("isConnected", () =>
1429
+ updateConnectViaWebSocketsButton()
1430
+ );
1431
+
1432
+ // CAMERA
1433
+ /** @type {HTMLSpanElement} */
1434
+ const isCameraAvailableSpan = document.getElementById("isCameraAvailable");
1435
+ device.addEventListener("connected", () => {
1436
+ isCameraAvailableSpan.innerText = device.hasCamera;
1437
+ });
1438
+
1439
+ /** @type {HTMLSpanElement} */
1440
+ const cameraStatusSpan = document.getElementById("cameraStatus");
1441
+ device.addEventListener("cameraStatus", () => {
1442
+ cameraStatusSpan.innerText = device.cameraStatus;
1443
+ });
1444
+
1445
+ /** @type {HTMLButtonElement} */
1446
+ const takePictureButton = document.getElementById("takePicture");
1447
+ takePictureButton.addEventListener("click", () => {
1448
+ if (device.cameraStatus == "idle") {
1449
+ device.takePicture();
1450
+ } else {
1451
+ device.stopCamera();
1452
+ }
1453
+ });
1454
+ device.addEventListener("connected", () => {
1455
+ updateTakePictureButton();
1456
+ });
1457
+ device.addEventListener("getSensorConfiguration", () => {
1458
+ updateTakePictureButton();
1459
+ });
1460
+ const updateTakePictureButton = () => {
1461
+ takePictureButton.disabled =
1462
+ !device.isConnected ||
1463
+ device.sensorConfiguration.camera == 0 ||
1464
+ device.cameraStatus != "idle";
1465
+ };
1466
+ device.addEventListener("cameraStatus", () => {
1467
+ updateTakePictureButton();
1468
+ });
1469
+
1470
+ /** @type {HTMLButtonElement} */
1471
+ const focusCameraButton = document.getElementById("focusCamera");
1472
+ focusCameraButton.addEventListener("click", () => {
1473
+ if (device.cameraStatus == "idle") {
1474
+ device.focusCamera();
1475
+ } else {
1476
+ device.stopCamera();
1477
+ }
1478
+ });
1479
+ device.addEventListener("connected", () => {
1480
+ updateFocusCameraButton();
1481
+ });
1482
+ device.addEventListener("getSensorConfiguration", () => {
1483
+ updateFocusCameraButton();
1484
+ });
1485
+ const updateFocusCameraButton = () => {
1486
+ focusCameraButton.disabled =
1487
+ !device.isConnected ||
1488
+ device.sensorConfiguration.camera == 0 ||
1489
+ device.cameraStatus != "idle";
1490
+ };
1491
+ device.addEventListener("cameraStatus", (event) => {
1492
+ updateFocusCameraButton();
1493
+ if (
1494
+ device.cameraStatus == "idle" &&
1495
+ event.message.previousCameraStatus == "focusing"
1496
+ ) {
1497
+ device.takePicture();
1498
+ }
1499
+ });
1500
+
1501
+ /** @type {HTMLButtonElement} */
1502
+ const sleepCameraButton = document.getElementById("sleepCamera");
1503
+ sleepCameraButton.addEventListener("click", () => {
1504
+ if (device.cameraStatus == "asleep") {
1505
+ device.wakeCamera();
1506
+ } else {
1507
+ device.sleepCamera();
1508
+ }
1509
+ });
1510
+ device.addEventListener("connected", () => {
1511
+ updateSleepCameraButton();
1512
+ });
1513
+ device.addEventListener("getSensorConfiguration", () => {
1514
+ updateSleepCameraButton();
1515
+ });
1516
+ const updateSleepCameraButton = () => {
1517
+ let disabled = !device.isConnected || !device.hasCamera;
1518
+ switch (device.cameraStatus) {
1519
+ case "asleep":
1520
+ sleepCameraButton.innerText = "wake camera";
1521
+ break;
1522
+ case "idle":
1523
+ sleepCameraButton.innerText = "sleep camera";
1524
+ break;
1525
+ default:
1526
+ disabled = true;
1527
+ break;
1528
+ }
1529
+ sleepCameraButton.disabled = disabled;
1530
+ };
1531
+ device.addEventListener("cameraStatus", () => {
1532
+ updateSleepCameraButton();
1533
+ });
1534
+
1535
+ /** @type {HTMLImageElement} */
1536
+ const cameraImage = document.getElementById("cameraImage");
1537
+ device.addEventListener("cameraImage", (event) => {
1538
+ cameraImage.src = event.message.url;
1539
+ });
1540
+
1541
+ /** @type {HTMLProgressElement} */
1542
+ const cameraImageProgress = document.getElementById("cameraImageProgress");
1543
+ device.addEventListener("cameraImageProgress", (event) => {
1544
+ if (event.message.type == "image") {
1545
+ cameraImageProgress.value = event.message.progress;
1546
+ }
1547
+ });
1548
+
1549
+ /** @type {HTMLInputElement} */
1550
+ const autoPictureCheckbox = document.getElementById("autoPicture");
1551
+ let autoPicture = autoPictureCheckbox.checked;
1552
+ autoPictureCheckbox.addEventListener("input", () => {
1553
+ autoPicture = autoPictureCheckbox.checked;
1554
+ });
1555
+ device.addEventListener("cameraImage", () => {
1556
+ if (autoPicture) {
1557
+ device.takePicture();
1558
+ }
1559
+ });
1560
+
1561
+ /** @type {HTMLPreElement} */
1562
+ const cameraConfigurationPre = document.getElementById(
1563
+ "cameraConfigurationPre"
1564
+ );
1565
+ device.addEventListener("getCameraConfiguration", () => {
1566
+ cameraConfigurationPre.textContent = JSON.stringify(
1567
+ device.cameraConfiguration,
1568
+ null,
1569
+ 2
1570
+ );
1571
+ });
1572
+
1573
+ const cameraConfigurationContainer = document.getElementById(
1574
+ "cameraConfiguration"
1575
+ );
1576
+ /** @type {HTMLTemplateElement} */
1577
+ const cameraConfigurationTypeTemplate = document.getElementById(
1578
+ "cameraConfigurationTypeTemplate"
1579
+ );
1580
+ BS.CameraConfigurationTypes.forEach((cameraConfigurationType) => {
1581
+ const cameraConfigurationTypeContainer =
1582
+ cameraConfigurationTypeTemplate.content
1583
+ .cloneNode(true)
1584
+ .querySelector(".cameraConfigurationType");
1585
+
1586
+ cameraConfigurationContainer.appendChild(cameraConfigurationTypeContainer);
1587
+
1588
+ cameraConfigurationTypeContainer.querySelector(".type").innerText =
1589
+ cameraConfigurationType;
1590
+
1591
+ /** @type {HTMLInputElement} */
1592
+ const input = cameraConfigurationTypeContainer.querySelector("input");
1593
+
1594
+ /** @type {HTMLSpanElement} */
1595
+ const span = cameraConfigurationTypeContainer.querySelector("span");
1596
+
1597
+ device.addEventListener("isConnected", () => {
1598
+ updateisInputDisabled();
1599
+ });
1600
+ device.addEventListener("cameraStatus", () => {
1601
+ updateisInputDisabled();
1602
+ });
1603
+ const updateisInputDisabled = () => {
1604
+ input.disabled =
1605
+ !device.isConnected || !device.hasCamera || device.cameraStatus != "idle";
1606
+ };
1607
+
1608
+ const updateInput = () => {
1609
+ const value = device.cameraConfiguration[cameraConfigurationType];
1610
+ span.innerText = value;
1611
+ input.value = value;
1612
+ };
1613
+
1614
+ device.addEventListener("connected", () => {
1615
+ if (!device.hasCamera) {
1616
+ return;
1617
+ }
1618
+ const range = device.cameraConfigurationRanges[cameraConfigurationType];
1619
+ input.min = range.min;
1620
+ input.max = range.max;
1621
+
1622
+ updateInput();
1623
+ });
1624
+
1625
+ device.addEventListener("getCameraConfiguration", () => {
1626
+ updateInput();
1627
+ });
1628
+
1629
+ input.addEventListener("change", () => {
1630
+ const value = Number(input.value);
1631
+ // console.log(`updating ${cameraConfigurationType} to ${value}`);
1632
+ device.setCameraConfiguration({
1633
+ [cameraConfigurationType]: value,
1634
+ });
1635
+ if (takePictureAfterUpdate) {
1636
+ device.addEventListener(
1637
+ "getCameraConfiguration",
1638
+ () => {
1639
+ setTimeout(() => device.takePicture()), 100;
1640
+ },
1641
+ { once: true }
1642
+ );
1643
+ }
1644
+ });
1645
+ });
1646
+
1647
+ /** @type {HTMLInputElement} */
1648
+ const takePictureAfterUpdateCheckbox = document.getElementById(
1649
+ "takePictureAfterUpdate"
1650
+ );
1651
+ let takePictureAfterUpdate = false;
1652
+ takePictureAfterUpdateCheckbox.addEventListener("input", () => {
1653
+ takePictureAfterUpdate = takePictureAfterUpdateCheckbox.checked;
1654
+ console.log({ takePictureAfterUpdate });
1655
+ });
1656
+
1657
+ let barcodeDetector;
1658
+ BarcodeDetector.getSupportedFormats().then((supportedFormats) => {
1659
+ console.log({ supportedFormats });
1660
+ // create new detector
1661
+ barcodeDetector = new BarcodeDetector({
1662
+ formats: supportedFormats,
1663
+ });
1664
+ });
1665
+ cameraImage.addEventListener("load", () => {
1666
+ if (barcodeDetector) {
1667
+ barcodeDetector
1668
+ .detect(cameraImage)
1669
+ .then((barcodes) => {
1670
+ barcodes.forEach((barcode) => console.log(barcode.rawValue));
1671
+ })
1672
+ .catch((err) => {
1673
+ console.log(err);
1674
+ });
1675
+ }
1676
+ });
1677
+
1678
+ /** @type {HTMLInputElement} */
1679
+ const cameraWhiteBalanceInput = document.getElementById("cameraWhiteBalance");
1680
+ const updateWhiteBalance = BS.ThrottleUtils.throttle(
1681
+ (config) => {
1682
+ if (device.cameraStatus != "idle") {
1683
+ return;
1684
+ }
1685
+
1686
+ device.setCameraConfiguration(config);
1687
+
1688
+ if (takePictureAfterUpdate) {
1689
+ device.addEventListener(
1690
+ "getCameraConfiguration",
1691
+ () => {
1692
+ setTimeout(() => device.takePicture()), 100;
1693
+ },
1694
+ { once: true }
1695
+ );
1696
+ }
1697
+ },
1698
+ 200,
1699
+ true
1700
+ );
1701
+ cameraWhiteBalanceInput.addEventListener("input", () => {
1702
+ let [redGain, greenGain, blueGain] = cameraWhiteBalanceInput.value
1703
+ .replace("#", "")
1704
+ .match(/.{1,2}/g)
1705
+ .map((value) => Number(`0x${value}`))
1706
+ .map((value) => value / 255)
1707
+ .map((value) => value * device.cameraConfigurationRanges.blueGain.max)
1708
+ .map((value) => Math.round(value));
1709
+
1710
+ updateWhiteBalance({ redGain, greenGain, blueGain });
1711
+ });
1712
+ const updateCameraWhiteBalanceInput = () => {
1713
+ if (!device.hasCamera) {
1714
+ return;
1715
+ }
1716
+ cameraWhiteBalanceInput.disabled =
1717
+ !device.isConnected || !device.hasCamera || device.cameraStatus != "idle";
1718
+
1719
+ const { redGain, blueGain, greenGain } = device.cameraConfiguration;
1720
+
1721
+ cameraWhiteBalanceInput.value = `#${[redGain, blueGain, greenGain]
1722
+ .map((value) => value / device.cameraConfigurationRanges.redGain.max)
1723
+ .map((value) => value * 255)
1724
+ .map((value) => Math.round(value))
1725
+ .map((value) => value.toString(16))
1726
+ .join("")}`;
1727
+ };
1728
+ device.addEventListener("connected", () => {
1729
+ updateCameraWhiteBalanceInput();
1730
+ });
1731
+ device.addEventListener("getCameraConfiguration", () => {
1732
+ updateCameraWhiteBalanceInput();
1733
+ });