brilliantsole 0.0.27 → 0.0.29

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 (202) hide show
  1. package/assets/3d/anchor.glb +0 -0
  2. package/assets/3d/coin.glb +0 -0
  3. package/assets/3d/glasses.glb +0 -0
  4. package/assets/audio/bounceMedium.wav +0 -0
  5. package/assets/audio/bounceStrong.wav +0 -0
  6. package/assets/audio/bounceWeak.wav +0 -0
  7. package/assets/audio/coin.wav +0 -0
  8. package/assets/audio/getUp.wav +0 -0
  9. package/assets/audio/grab.wav +0 -0
  10. package/assets/audio/kick.wav +0 -0
  11. package/assets/audio/platterFadeIn old.wav +0 -0
  12. package/assets/audio/platterFadeIn.wav +0 -0
  13. package/assets/audio/platterFadeOut.wav +0 -0
  14. package/assets/audio/punch.wav +0 -0
  15. package/assets/audio/punchSqueak.wav +0 -0
  16. package/assets/audio/purr.wav +0 -0
  17. package/assets/audio/purrFadeOut.wav +0 -0
  18. package/assets/audio/release.wav +0 -0
  19. package/assets/audio/splat.wav +0 -0
  20. package/assets/audio/stomp.wav +0 -0
  21. package/build/brilliantsole.cjs +3091 -741
  22. package/build/brilliantsole.cjs.map +1 -1
  23. package/build/brilliantsole.js +2759 -709
  24. package/build/brilliantsole.js.map +1 -1
  25. package/build/brilliantsole.ls.js +2602 -543
  26. package/build/brilliantsole.ls.js.map +1 -1
  27. package/build/brilliantsole.min.js +1 -1
  28. package/build/brilliantsole.min.js.map +1 -1
  29. package/build/brilliantsole.module.d.ts +295 -65
  30. package/build/brilliantsole.module.js +2749 -710
  31. package/build/brilliantsole.module.js.map +1 -1
  32. package/build/brilliantsole.module.min.d.ts +295 -65
  33. package/build/brilliantsole.module.min.js +1 -1
  34. package/build/brilliantsole.module.min.js.map +1 -1
  35. package/build/brilliantsole.node.module.d.ts +289 -62
  36. package/build/brilliantsole.node.module.js +3080 -742
  37. package/build/brilliantsole.node.module.js.map +1 -1
  38. package/build/dts/BS-output.d.ts +10 -0
  39. package/build/dts/BS.d.ts +21 -8
  40. package/build/dts/CameraManager.d.ts +72 -0
  41. package/build/dts/Device.d.ts +64 -13
  42. package/build/dts/DeviceInformationManager.d.ts +4 -4
  43. package/build/dts/DeviceManager.d.ts +2 -0
  44. package/build/dts/FileTransferManager.d.ts +18 -8
  45. package/build/dts/InformationManager.d.ts +2 -0
  46. package/build/dts/MicrophoneManager.d.ts +88 -0
  47. package/build/dts/TfliteManager.d.ts +22 -2
  48. package/build/dts/WifiManager.d.ts +61 -0
  49. package/build/dts/connection/BaseConnectionManager.d.ts +35 -3
  50. package/build/dts/connection/ClientConnectionManager.d.ts +7 -2
  51. package/build/dts/connection/bluetooth/NobleConnectionManager.d.ts +2 -1
  52. package/build/dts/connection/bluetooth/WebBluetoothConnectionManager.d.ts +1 -0
  53. package/build/dts/connection/bluetooth/bluetoothUUIDs.d.ts +2 -2
  54. package/build/dts/connection/udp/UDPConnectionManager.d.ts +28 -0
  55. package/build/dts/connection/webSocket/WebSocketConnectionManager.d.ts +25 -0
  56. package/build/dts/devicePair/DevicePair.d.ts +5 -5
  57. package/build/dts/scanner/BaseScanner.d.ts +4 -1
  58. package/build/dts/scanner/NobleScanner.d.ts +2 -1
  59. package/build/dts/sensor/MotionSensorDataManager.d.ts +5 -2
  60. package/build/dts/sensor/SensorDataManager.d.ts +5 -4
  61. package/build/dts/server/BaseClient.d.ts +5 -3
  62. package/build/dts/server/ServerUtils.d.ts +1 -1
  63. package/build/dts/server/websocket/WebSocketUtils.d.ts +1 -1
  64. package/build/dts/utils/AudioUtils.d.ts +2 -0
  65. package/build/dts/utils/Console.d.ts +2 -0
  66. package/build/dts/utils/ThrottleUtils.d.ts +2 -0
  67. package/build/dts/vibration/VibrationManager.d.ts +19 -2
  68. package/build/index.d.ts +292 -62
  69. package/build/index.node.d.ts +286 -59
  70. package/examples/3d/scene.html +19 -5
  71. package/examples/3d-generic/index.html +144 -0
  72. package/examples/3d-generic/script.js +266 -0
  73. package/examples/basic/index.html +267 -17
  74. package/examples/basic/script.js +958 -105
  75. package/examples/camera/barcode-detector.js +109 -0
  76. package/examples/camera/depth-estimation.js +71 -0
  77. package/examples/camera/face-detector.js +119 -0
  78. package/examples/camera/face-landmark.js +111 -0
  79. package/examples/camera/gesture-recognition.js +97 -0
  80. package/examples/camera/hand-landmark.js +74 -0
  81. package/examples/camera/image-segmentation.js +98 -0
  82. package/examples/camera/image-to-text.js +43 -0
  83. package/examples/camera/image-upscale.js +75 -0
  84. package/examples/camera/index.html +129 -0
  85. package/examples/camera/object-detection.js +98 -0
  86. package/examples/camera/pose-landmark.js +60 -0
  87. package/examples/camera/script.js +316 -0
  88. package/examples/camera/utils.js +165 -0
  89. package/examples/camera/yolo-tiny.js +54 -0
  90. package/examples/camera/yolo.js +119 -0
  91. package/examples/edge-impulse/script.js +157 -48
  92. package/examples/edge-impulse-test/README.md +11 -0
  93. package/examples/edge-impulse-test/edge-impulse-standalone.js +7228 -0
  94. package/examples/edge-impulse-test/edge-impulse-standalone.wasm +0 -0
  95. package/examples/edge-impulse-test/index.html +75 -0
  96. package/examples/edge-impulse-test/run-impulse.js +135 -0
  97. package/examples/edge-impulse-test/script.js +200 -0
  98. package/examples/glasses-gestures/README.md +11 -0
  99. package/examples/glasses-gestures/edge-impulse-standalone.js +7228 -0
  100. package/examples/glasses-gestures/edge-impulse-standalone.wasm +0 -0
  101. package/examples/glasses-gestures/index.html +69 -0
  102. package/examples/glasses-gestures/run-impulse.js +135 -0
  103. package/examples/glasses-gestures/script.js +226 -0
  104. package/examples/gloves/edge-impulse-standalone.js +7228 -0
  105. package/examples/gloves/edge-impulse-standalone.wasm +0 -0
  106. package/examples/gloves/index.html +4 -1
  107. package/examples/gloves/run-impulse.js +135 -0
  108. package/examples/gloves/script.js +367 -51
  109. package/examples/graph/script.js +94 -37
  110. package/examples/microphone/gender.js +54 -0
  111. package/examples/microphone/index.html +102 -0
  112. package/examples/microphone/script.js +394 -0
  113. package/examples/microphone/utils.js +45 -0
  114. package/examples/microphone/whisper-realtime.js +166 -0
  115. package/examples/microphone/whisper.js +132 -0
  116. package/examples/punch/index.html +135 -0
  117. package/examples/punch/punch.tflite +0 -0
  118. package/examples/punch/script.js +169 -0
  119. package/examples/server/index.html +98 -22
  120. package/examples/server/script.js +317 -109
  121. package/examples/ukaton-firmware-update/merged-firmware.bin +0 -0
  122. package/examples/utils/aframe/aframe-master.min.js +2 -0
  123. package/examples/utils/aframe/bs-vibration.js +150 -0
  124. package/examples/utils/aframe/force-pushable.js +80 -0
  125. package/examples/utils/aframe/grabbable-anchor.js +46 -0
  126. package/examples/utils/aframe/grabbable-listener.js +31 -0
  127. package/examples/utils/aframe/grabbable-physics-body.js +190 -0
  128. package/examples/utils/aframe/grow-shrink.js +25 -0
  129. package/examples/utils/aframe/hand-punch.js +119 -0
  130. package/examples/utils/aframe/my-obb-collider.js +293 -0
  131. package/examples/utils/aframe/occlude-hand-tracking-controls.js +47 -0
  132. package/examples/utils/aframe/occlude-mesh.js +42 -0
  133. package/examples/utils/aframe/palm-up-detector.js +47 -0
  134. package/examples/utils/aframe/shadow-material.js +20 -0
  135. package/examples/utils/aframe/soft-shadow-light.js +9 -0
  136. package/examples/webxr-2/assets/3d/soccerBall.glb +0 -0
  137. package/examples/webxr-2/assets/audio/shellBounce.wav +0 -0
  138. package/examples/webxr-2/assets/audio/shellHit.wav +0 -0
  139. package/examples/webxr-2/assets/audio/shellKick.wav +0 -0
  140. package/examples/webxr-2/assets/audio/soccerBounce.wav +0 -0
  141. package/examples/webxr-2/assets/audio/soccerKick.mp3 +0 -0
  142. package/examples/webxr-2/assets/images/shellTexture.png +0 -0
  143. package/examples/webxr-2/components/bs-ankle.js +337 -0
  144. package/examples/webxr-2/components/coin.js +84 -0
  145. package/examples/webxr-2/components/custom-wrap.js +17 -0
  146. package/examples/webxr-2/components/goomba.js +3250 -0
  147. package/examples/webxr-2/components/init-shell-material.js +215 -0
  148. package/examples/webxr-2/components/platter.js +172 -0
  149. package/examples/webxr-2/components/shell.js +374 -0
  150. package/examples/webxr-2/components/soccer-ball.js +250 -0
  151. package/examples/webxr-2/components/squashed-goomba.js +249 -0
  152. package/examples/webxr-2/edge-impulse-standalone.js +7228 -0
  153. package/examples/webxr-2/edge-impulse-standalone.wasm +0 -0
  154. package/examples/webxr-2/index.html +996 -0
  155. package/examples/webxr-2/kick.tflite +0 -0
  156. package/examples/webxr-2/kick2.tflite +0 -0
  157. package/examples/webxr-2/run-impulse.js +135 -0
  158. package/examples/webxr-2/script.js +384 -0
  159. package/examples/webxr-3/components/bs-camera.js +65 -0
  160. package/examples/webxr-3/index.html +134 -0
  161. package/examples/webxr-3/script.js +432 -0
  162. package/package.json +2 -1
  163. package/src/.prettierrc +4 -0
  164. package/src/BS.ts +79 -8
  165. package/src/CameraManager.ts +497 -0
  166. package/src/Device.ts +691 -86
  167. package/src/DeviceInformationManager.ts +19 -10
  168. package/src/DeviceManager.ts +85 -25
  169. package/src/FileTransferManager.ts +145 -20
  170. package/src/InformationManager.ts +40 -15
  171. package/src/MicrophoneManager.ts +599 -0
  172. package/src/TfliteManager.ts +171 -25
  173. package/src/WifiManager.ts +323 -0
  174. package/src/connection/BaseConnectionManager.ts +130 -30
  175. package/src/connection/ClientConnectionManager.ts +34 -10
  176. package/src/connection/bluetooth/BluetoothConnectionManager.ts +8 -2
  177. package/src/connection/bluetooth/NobleConnectionManager.ts +147 -41
  178. package/src/connection/bluetooth/WebBluetoothConnectionManager.ts +99 -34
  179. package/src/connection/bluetooth/bluetoothUUIDs.ts +40 -13
  180. package/src/connection/udp/UDPConnectionManager.ts +356 -0
  181. package/src/connection/websocket/WebSocketConnectionManager.ts +282 -0
  182. package/src/devicePair/DevicePair.ts +95 -25
  183. package/src/devicePair/DevicePairPressureSensorDataManager.ts +27 -7
  184. package/src/scanner/BaseScanner.ts +49 -11
  185. package/src/scanner/NobleScanner.ts +76 -14
  186. package/src/sensor/MotionSensorDataManager.ts +21 -6
  187. package/src/sensor/PressureSensorDataManager.ts +37 -8
  188. package/src/sensor/SensorConfigurationManager.ts +73 -22
  189. package/src/sensor/SensorDataManager.ts +109 -23
  190. package/src/server/BaseClient.ts +150 -36
  191. package/src/server/BaseServer.ts +50 -2
  192. package/src/server/ServerUtils.ts +39 -9
  193. package/src/server/udp/UDPServer.ts +73 -22
  194. package/src/server/udp/UDPUtils.ts +9 -2
  195. package/src/server/websocket/WebSocketClient.ts +27 -7
  196. package/src/server/websocket/WebSocketUtils.ts +4 -2
  197. package/src/utils/AudioUtils.ts +65 -0
  198. package/src/utils/Console.ts +62 -9
  199. package/src/utils/ParseUtils.ts +24 -5
  200. package/src/utils/ThrottleUtils.ts +62 -0
  201. package/src/utils/Timer.ts +1 -1
  202. 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,11 @@ 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
+ );
118
153
  });
119
154
 
120
155
  // MTU
@@ -147,7 +182,9 @@ device.addEventListener("getBatteryCurrent", () => {
147
182
  });
148
183
 
149
184
  /** @type {HTMLButtonElement} */
150
- const updateBatteryCurrentButton = document.getElementById("updateBatteryCurrent");
185
+ const updateBatteryCurrentButton = document.getElementById(
186
+ "updateBatteryCurrent"
187
+ );
151
188
  device.addEventListener("isConnected", () => {
152
189
  updateBatteryCurrentButton.disabled = !device.isConnected;
153
190
  });
@@ -231,22 +268,33 @@ setTypeButton.addEventListener("click", () => {
231
268
  // SENSOR CONFIGURATION
232
269
 
233
270
  /** @type {HTMLPreElement} */
234
- const sensorConfigurationPre = document.getElementById("sensorConfigurationPre");
271
+ const sensorConfigurationPre = document.getElementById(
272
+ "sensorConfigurationPre"
273
+ );
235
274
  device.addEventListener("getSensorConfiguration", () => {
236
- sensorConfigurationPre.textContent = JSON.stringify(device.sensorConfiguration, null, 2);
275
+ sensorConfigurationPre.textContent = JSON.stringify(
276
+ device.sensorConfiguration,
277
+ null,
278
+ 2
279
+ );
237
280
  });
238
281
 
239
282
  /** @type {HTMLTemplateElement} */
240
- const sensorTypeConfigurationTemplate = document.getElementById("sensorTypeConfigurationTemplate");
283
+ const sensorTypeConfigurationTemplate = document.getElementById(
284
+ "sensorTypeConfigurationTemplate"
285
+ );
241
286
  BS.SensorTypes.forEach((sensorType) => {
242
287
  /** @type {HTMLElement} */
243
- const sensorTypeConfigurationContainer = sensorTypeConfigurationTemplate.content
244
- .cloneNode(true)
245
- .querySelector(".sensorTypeConfiguration");
246
- sensorTypeConfigurationContainer.querySelector(".sensorType").innerText = sensorType;
288
+ const sensorTypeConfigurationContainer =
289
+ sensorTypeConfigurationTemplate.content
290
+ .cloneNode(true)
291
+ .querySelector(".sensorTypeConfiguration");
292
+ sensorTypeConfigurationContainer.querySelector(".sensorType").innerText =
293
+ sensorType;
247
294
 
248
295
  /** @type {HTMLInputElement} */
249
- const sensorRateInput = sensorTypeConfigurationContainer.querySelector(".sensorRate");
296
+ const sensorRateInput =
297
+ sensorTypeConfigurationContainer.querySelector(".sensorRate");
250
298
  sensorRateInput.value = 0;
251
299
  sensorRateInput.max = BS.MaxSensorRate;
252
300
  sensorRateInput.step = BS.SensorRateStep;
@@ -264,18 +312,23 @@ BS.SensorTypes.forEach((sensorType) => {
264
312
  }
265
313
  });
266
314
 
267
- sensorTypeConfigurationTemplate.parentElement.appendChild(sensorTypeConfigurationContainer);
315
+ sensorTypeConfigurationTemplate.parentElement.appendChild(
316
+ sensorTypeConfigurationContainer
317
+ );
268
318
  sensorTypeConfigurationContainer.dataset.sensorType = sensorType;
269
319
  });
270
320
  device.addEventListener("getSensorConfiguration", () => {
271
321
  for (const sensorType in device.sensorConfiguration) {
272
- document.querySelector(`.sensorTypeConfiguration[data-sensor-type="${sensorType}"] .input`).value =
273
- device.sensorConfiguration[sensorType];
322
+ document.querySelector(
323
+ `.sensorTypeConfiguration[data-sensor-type="${sensorType}"] .input`
324
+ ).value = device.sensorConfiguration[sensorType];
274
325
  }
275
326
  });
276
327
  device.addEventListener("isConnected", () => {
277
328
  for (const sensorType in device.sensorConfiguration) {
278
- document.querySelector(`[data-sensor-type="${sensorType}"] .input`).disabled = !device.isConnected;
329
+ document.querySelector(
330
+ `[data-sensor-type="${sensorType}"] .input`
331
+ ).disabled = !device.isConnected;
279
332
  }
280
333
  });
281
334
 
@@ -290,9 +343,13 @@ resetPressureRangeButton.addEventListener("click", () => {
290
343
  // SENSOR DATA
291
344
 
292
345
  /** @type {HTMLTemplateElement} */
293
- const sensorTypeDataTemplate = document.getElementById("sensorTypeDataTemplate");
346
+ const sensorTypeDataTemplate = document.getElementById(
347
+ "sensorTypeDataTemplate"
348
+ );
294
349
  BS.SensorTypes.forEach((sensorType) => {
295
- const sensorTypeDataContainer = sensorTypeDataTemplate.content.cloneNode(true).querySelector(".sensorTypeData");
350
+ const sensorTypeDataContainer = sensorTypeDataTemplate.content
351
+ .cloneNode(true)
352
+ .querySelector(".sensorTypeData");
296
353
  sensorTypeDataContainer.querySelector(".sensorType").innerText = sensorType;
297
354
 
298
355
  /** @type {HTMLPreElement} */
@@ -311,38 +368,52 @@ BS.SensorTypes.forEach((sensorType) => {
311
368
  const vibrationTemplate = document.getElementById("vibrationTemplate");
312
369
  {
313
370
  /** @type {HTMLInputElement} */
314
- const waveformEffectSequenceLoopCountInput = vibrationTemplate.content.querySelector(
315
- ".waveformEffect .sequenceLoopCount"
316
- );
317
- waveformEffectSequenceLoopCountInput.max = BS.MaxVibrationWaveformEffectSequenceLoopCount;
371
+ const waveformEffectSequenceLoopCountInput =
372
+ vibrationTemplate.content.querySelector(
373
+ ".waveformEffect .sequenceLoopCount"
374
+ );
375
+ waveformEffectSequenceLoopCountInput.max =
376
+ BS.MaxVibrationWaveformEffectSequenceLoopCount;
318
377
  }
319
378
  /** @type {HTMLTemplateElement} */
320
- const vibrationLocationTemplate = document.getElementById("vibrationLocationTemplate");
379
+ const vibrationLocationTemplate = document.getElementById(
380
+ "vibrationLocationTemplate"
381
+ );
321
382
 
322
383
  /** @type {HTMLTemplateElement} */
323
- const waveformEffectSegmentTemplate = document.getElementById("waveformEffectSegmentTemplate");
384
+ const waveformEffectSegmentTemplate = document.getElementById(
385
+ "waveformEffectSegmentTemplate"
386
+ );
324
387
  {
325
388
  /** @type {HTMLSelectElement} */
326
- const waveformEffectSelect = waveformEffectSegmentTemplate.content.querySelector(".effect");
389
+ const waveformEffectSelect =
390
+ waveformEffectSegmentTemplate.content.querySelector(".effect");
327
391
  const waveformEffectOptgroup = waveformEffectSelect.querySelector("optgroup");
328
392
  BS.VibrationWaveformEffects.forEach((waveformEffect) => {
329
393
  waveformEffectOptgroup.appendChild(new Option(waveformEffect));
330
394
  });
331
395
 
332
396
  /** @type {HTMLInputElement} */
333
- const waveformEffectSegmentDelayInput = waveformEffectSegmentTemplate.content.querySelector(".delay");
334
- waveformEffectSegmentDelayInput.max = BS.MaxVibrationWaveformEffectSegmentDelay;
397
+ const waveformEffectSegmentDelayInput =
398
+ waveformEffectSegmentTemplate.content.querySelector(".delay");
399
+ waveformEffectSegmentDelayInput.max =
400
+ BS.MaxVibrationWaveformEffectSegmentDelay;
335
401
 
336
402
  /** @type {HTMLInputElement} */
337
- const waveformEffectLoopCountInput = waveformEffectSegmentTemplate.content.querySelector(".loopCount");
338
- waveformEffectLoopCountInput.max = BS.MaxVibrationWaveformEffectSegmentLoopCount;
403
+ const waveformEffectLoopCountInput =
404
+ waveformEffectSegmentTemplate.content.querySelector(".loopCount");
405
+ waveformEffectLoopCountInput.max =
406
+ BS.MaxVibrationWaveformEffectSegmentLoopCount;
339
407
  }
340
408
 
341
409
  /** @type {HTMLTemplateElement} */
342
- const waveformSegmentTemplate = document.getElementById("waveformSegmentTemplate");
410
+ const waveformSegmentTemplate = document.getElementById(
411
+ "waveformSegmentTemplate"
412
+ );
343
413
  {
344
414
  /** @type {HTMLInputElement} */
345
- const waveformDurationSegmentInput = waveformSegmentTemplate.content.querySelector(".duration");
415
+ const waveformDurationSegmentInput =
416
+ waveformSegmentTemplate.content.querySelector(".duration");
346
417
  waveformDurationSegmentInput.max = BS.MaxVibrationWaveformSegmentDuration;
347
418
  }
348
419
 
@@ -350,7 +421,9 @@ const waveformSegmentTemplate = document.getElementById("waveformSegmentTemplate
350
421
  const addVibrationButton = document.getElementById("addVibration");
351
422
  addVibrationButton.addEventListener("click", () => {
352
423
  /** @type {HTMLElement} */
353
- const vibrationContainer = vibrationTemplate.content.cloneNode(true).querySelector(".vibration");
424
+ const vibrationContainer = vibrationTemplate.content
425
+ .cloneNode(true)
426
+ .querySelector(".vibration");
354
427
 
355
428
  /** @type {HTMLButtonElement} */
356
429
  const deleteButton = vibrationContainer.querySelector(".delete");
@@ -360,25 +433,33 @@ addVibrationButton.addEventListener("click", () => {
360
433
  });
361
434
 
362
435
  /** @type {HTMLUListElement} */
363
- const vibrationLocationsContainer = vibrationContainer.querySelector(".locations");
364
- BS.VibrationLocations.forEach((vibrationLocation) => {
436
+ const vibrationLocationsContainer =
437
+ vibrationContainer.querySelector(".locations");
438
+ device.vibrationLocations.forEach((vibrationLocation) => {
365
439
  const vibrationLocationContainer = vibrationLocationTemplate.content
366
440
  .cloneNode(true)
367
441
  .querySelector(".vibrationLocation");
368
- vibrationLocationContainer.querySelector("span").innerText = vibrationLocation;
369
- vibrationLocationContainer.querySelector("input").dataset.vibrationLocation = vibrationLocation;
442
+ vibrationLocationContainer.querySelector("span").innerText =
443
+ vibrationLocation;
444
+ vibrationLocationContainer.querySelector(
445
+ "input"
446
+ ).dataset.vibrationLocation = vibrationLocation;
370
447
  vibrationLocationsContainer.appendChild(vibrationLocationContainer);
371
448
  });
372
449
 
373
450
  /** @type {HTMLElement} */
374
- const waveformEffectContainer = vibrationContainer.querySelector(".waveformEffect");
451
+ const waveformEffectContainer =
452
+ vibrationContainer.querySelector(".waveformEffect");
375
453
  /** @type {HTMLUListElement} */
376
- const waveformEffectSegmentsContainer = waveformEffectContainer.querySelector(".segments");
454
+ const waveformEffectSegmentsContainer =
455
+ waveformEffectContainer.querySelector(".segments");
377
456
  /** @type {HTMLButtonElement} */
378
- const addWaveformEffectSegmentButton = waveformEffectContainer.querySelector(".add");
457
+ const addWaveformEffectSegmentButton =
458
+ waveformEffectContainer.querySelector(".add");
379
459
  const updateAddWaveformEffectSegmentButton = () => {
380
460
  addWaveformEffectSegmentButton.disabled =
381
- waveformEffectSegmentsContainer.children.length >= BS.MaxNumberOfVibrationWaveformEffectSegments;
461
+ waveformEffectSegmentsContainer.children.length >=
462
+ BS.MaxNumberOfVibrationWaveformEffectSegments;
382
463
  };
383
464
  addWaveformEffectSegmentButton.addEventListener("click", () => {
384
465
  /** @type {HTMLElement} */
@@ -386,11 +467,14 @@ addVibrationButton.addEventListener("click", () => {
386
467
  .cloneNode(true)
387
468
  .querySelector(".waveformEffectSegment");
388
469
 
389
- const effectContainer = waveformEffectSegmentContainer.querySelector(".effect").parentElement;
390
- const delayContainer = waveformEffectSegmentContainer.querySelector(".delay").parentElement;
470
+ const effectContainer =
471
+ waveformEffectSegmentContainer.querySelector(".effect").parentElement;
472
+ const delayContainer =
473
+ waveformEffectSegmentContainer.querySelector(".delay").parentElement;
391
474
 
392
475
  /** @type {HTMLSelectElement} */
393
- const waveformEffectTypeSelect = waveformEffectSegmentContainer.querySelector(".type");
476
+ const waveformEffectTypeSelect =
477
+ waveformEffectSegmentContainer.querySelector(".type");
394
478
  waveformEffectTypeSelect.addEventListener("input", () => {
395
479
  let shouldShowEffectContainer = false;
396
480
  let shouldShowDelayContainer = false;
@@ -403,7 +487,9 @@ addVibrationButton.addEventListener("click", () => {
403
487
  shouldShowDelayContainer = true;
404
488
  break;
405
489
  default:
406
- throw Error(`uncaught waveformEffectTypeSelect value "${waveformEffectTypeSelect.value}"`);
490
+ throw Error(
491
+ `uncaught waveformEffectTypeSelect value "${waveformEffectTypeSelect.value}"`
492
+ );
407
493
  }
408
494
 
409
495
  effectContainer.style.display = shouldShowEffectContainer ? "" : "none";
@@ -411,10 +497,12 @@ addVibrationButton.addEventListener("click", () => {
411
497
  });
412
498
  waveformEffectTypeSelect.dispatchEvent(new Event("input"));
413
499
 
414
- waveformEffectSegmentContainer.querySelector(".delete").addEventListener("click", () => {
415
- waveformEffectSegmentContainer.remove();
416
- updateAddWaveformEffectSegmentButton();
417
- });
500
+ waveformEffectSegmentContainer
501
+ .querySelector(".delete")
502
+ .addEventListener("click", () => {
503
+ waveformEffectSegmentContainer.remove();
504
+ updateAddWaveformEffectSegmentButton();
505
+ });
418
506
 
419
507
  waveformEffectSegmentsContainer.appendChild(waveformEffectSegmentContainer);
420
508
  updateAddWaveformEffectSegmentButton();
@@ -423,22 +511,28 @@ addVibrationButton.addEventListener("click", () => {
423
511
  /** @type {HTMLElement} */
424
512
  const waveformContainer = vibrationContainer.querySelector(".waveform");
425
513
  /** @type {HTMLUListElement} */
426
- const waveformSegmentsContainer = waveformContainer.querySelector(".segments");
514
+ const waveformSegmentsContainer =
515
+ waveformContainer.querySelector(".segments");
427
516
 
428
517
  /** @type {HTMLButtonElement} */
429
518
  const addWaveformSegmentButton = waveformContainer.querySelector(".add");
430
519
  const updateAddWaveformSegmentButton = () => {
431
520
  addWaveformSegmentButton.disabled =
432
- waveformSegmentsContainer.children.length >= BS.MaxNumberOfVibrationWaveformSegments;
521
+ waveformSegmentsContainer.children.length >=
522
+ BS.MaxNumberOfVibrationWaveformSegments;
433
523
  };
434
524
  addWaveformSegmentButton.addEventListener("click", () => {
435
525
  /** @type {HTMLElement} */
436
- const waveformSegmentContainer = waveformSegmentTemplate.content.cloneNode(true).querySelector(".waveformSegment");
526
+ const waveformSegmentContainer = waveformSegmentTemplate.content
527
+ .cloneNode(true)
528
+ .querySelector(".waveformSegment");
437
529
 
438
- waveformSegmentContainer.querySelector(".delete").addEventListener("click", () => {
439
- waveformSegmentContainer.remove();
440
- updateAddWaveformSegmentButton();
441
- });
530
+ waveformSegmentContainer
531
+ .querySelector(".delete")
532
+ .addEventListener("click", () => {
533
+ waveformSegmentContainer.remove();
534
+ updateAddWaveformSegmentButton();
535
+ });
442
536
 
443
537
  waveformSegmentsContainer.appendChild(waveformSegmentContainer);
444
538
  updateAddWaveformSegmentButton();
@@ -447,7 +541,8 @@ addVibrationButton.addEventListener("click", () => {
447
541
  /** @type {HTMLSelectElement} */
448
542
  const vibrationTypeSelect = vibrationContainer.querySelector(".type");
449
543
  /** @type {HTMLOptGroupElement} */
450
- const vibrationTypeSelectOptgroup = vibrationTypeSelect.querySelector("optgroup");
544
+ const vibrationTypeSelectOptgroup =
545
+ vibrationTypeSelect.querySelector("optgroup");
451
546
  BS.VibrationTypes.forEach((vibrationType) => {
452
547
  vibrationTypeSelectOptgroup.appendChild(new Option(vibrationType));
453
548
  });
@@ -469,7 +564,9 @@ addVibrationButton.addEventListener("click", () => {
469
564
  throw Error(`invalid vibrationType "${vibrationType}"`);
470
565
  }
471
566
 
472
- waveformEffectContainer.style.display = showWaveformEffectContainer ? "" : "none";
567
+ waveformEffectContainer.style.display = showWaveformEffectContainer
568
+ ? ""
569
+ : "none";
473
570
  waveformContainer.style.display = showWaveformContainer ? "" : "none";
474
571
  });
475
572
  vibrationTypeSelect.dispatchEvent(new Event("input"));
@@ -484,40 +581,60 @@ triggerVibrationsButton.addEventListener("click", () => {
484
581
  /** @type {BS.VibrationConfiguration[]} */
485
582
  let vibrationConfigurations = [];
486
583
  Array.from(vibrationTemplate.parentElement.querySelectorAll(".vibration"))
487
- .filter((vibrationContainer) => vibrationContainer.querySelector(".shouldTrigger").checked)
584
+ .filter(
585
+ (vibrationContainer) =>
586
+ vibrationContainer.querySelector(".shouldTrigger").checked
587
+ )
488
588
  .forEach((vibrationContainer) => {
489
589
  /** @type {BS.VibrationConfiguration} */
490
590
  const vibrationConfiguration = {
491
591
  locations: [],
492
592
  };
493
- Array.from(vibrationContainer.querySelectorAll(`[data-vibration-location]`))
593
+ Array.from(
594
+ vibrationContainer.querySelectorAll(`[data-vibration-location]`)
595
+ )
494
596
  .filter((input) => input.checked)
495
597
  .forEach((input) => {
496
- vibrationConfiguration.locations.push(input.dataset.vibrationLocation);
598
+ vibrationConfiguration.locations.push(
599
+ input.dataset.vibrationLocation
600
+ );
497
601
  });
498
602
  if (vibrationConfiguration.locations.length == 0) {
499
603
  return;
500
604
  }
501
605
 
502
- vibrationConfiguration.type = vibrationContainer.querySelector("select.type").value;
606
+ vibrationConfiguration.type =
607
+ vibrationContainer.querySelector("select.type").value;
503
608
  switch (vibrationConfiguration.type) {
504
609
  case "waveformEffect":
505
610
  vibrationConfiguration.segments = Array.from(
506
- vibrationContainer.querySelectorAll(".waveformEffect .waveformEffectSegment")
611
+ vibrationContainer.querySelectorAll(
612
+ ".waveformEffect .waveformEffectSegment"
613
+ )
507
614
  ).map((waveformEffectSegmentContainer) => {
508
615
  /** @type {BS.VibrationWaveformEffectSegment} */
509
616
  const waveformEffectSegment = {
510
- loopCount: Number(waveformEffectSegmentContainer.querySelector(".loopCount").value),
617
+ loopCount: Number(
618
+ waveformEffectSegmentContainer.querySelector(".loopCount").value
619
+ ),
511
620
  };
512
- if (waveformEffectSegmentContainer.querySelector(".type").value == "effect") {
513
- waveformEffectSegment.effect = waveformEffectSegmentContainer.querySelector(".effect").value;
621
+ if (
622
+ waveformEffectSegmentContainer.querySelector(".type").value ==
623
+ "effect"
624
+ ) {
625
+ waveformEffectSegment.effect =
626
+ waveformEffectSegmentContainer.querySelector(".effect").value;
514
627
  } else {
515
- waveformEffectSegment.delay = Number(waveformEffectSegmentContainer.querySelector(".delay").value);
628
+ waveformEffectSegment.delay = Number(
629
+ waveformEffectSegmentContainer.querySelector(".delay").value
630
+ );
516
631
  }
517
632
  return waveformEffectSegment;
518
633
  });
519
634
  vibrationConfiguration.loopCount = Number(
520
- vibrationContainer.querySelector(".waveformEffect .sequenceLoopCount").value
635
+ vibrationContainer.querySelector(
636
+ ".waveformEffect .sequenceLoopCount"
637
+ ).value
521
638
  );
522
639
  break;
523
640
  case "waveform":
@@ -525,8 +642,12 @@ triggerVibrationsButton.addEventListener("click", () => {
525
642
  vibrationContainer.querySelectorAll(".waveform .waveformSegment")
526
643
  ).map((waveformSegmentContainer) => {
527
644
  return {
528
- amplitude: Number(waveformSegmentContainer.querySelector(".amplitude").value),
529
- duration: Number(waveformSegmentContainer.querySelector(".duration").value),
645
+ amplitude: Number(
646
+ waveformSegmentContainer.querySelector(".amplitude").value
647
+ ),
648
+ duration: Number(
649
+ waveformSegmentContainer.querySelector(".duration").value
650
+ ),
530
651
  };
531
652
  });
532
653
  break;
@@ -546,7 +667,8 @@ device.addEventListener("isConnected", () => {
546
667
 
547
668
  function updateTriggerVibrationsButtonDisabled() {
548
669
  triggerVibrationsButton.disabled =
549
- !device.isConnected || vibrationTemplate.parentElement.querySelectorAll(".vibration").length == 0;
670
+ !device.isConnected ||
671
+ vibrationTemplate.parentElement.querySelectorAll(".vibration").length == 0;
550
672
  }
551
673
 
552
674
  // FILE TRANSFER
@@ -587,14 +709,28 @@ fileTransferTypesSelect.addEventListener("input", () => {
587
709
  case "tflite":
588
710
  fileInput.accept = ".tflite";
589
711
  break;
712
+ case "wifiServerCert":
713
+ fileInput.accept = ".crt";
714
+ break;
715
+ case "wifiServerKey":
716
+ fileInput.accept = ".key";
717
+ break;
590
718
  }
591
719
  });
592
720
  /** @type {HTMLOptGroupElement} */
593
- const fileTransferTypesOptgroup = fileTransferTypesSelect.querySelector("optgroup");
721
+ const fileTransferTypesOptgroup =
722
+ fileTransferTypesSelect.querySelector("optgroup");
594
723
  BS.FileTypes.forEach((fileType) => {
595
724
  fileTransferTypesOptgroup.appendChild(new Option(fileType));
596
725
  });
597
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
+ });
598
734
 
599
735
  /** @type {HTMLProgressElement} */
600
736
  const fileTransferProgress = document.getElementById("fileTransferProgress");
@@ -627,7 +763,8 @@ toggleFileTransferButton.addEventListener("click", async () => {
627
763
  }
628
764
  });
629
765
  const updateToggleFileTransferButton = () => {
630
- const enabled = device.isConnected && (file || fileTransferDirection == "receive");
766
+ const enabled =
767
+ device.isConnected && (file || fileTransferDirection == "receive");
631
768
  toggleFileTransferButton.disabled = !enabled;
632
769
 
633
770
  /** @type {String} */
@@ -655,7 +792,9 @@ device.addEventListener("fileTransferStatus", () => {
655
792
  /** @type {BS.FileTransferDirection} */
656
793
  let fileTransferDirection;
657
794
  /** @type {HTMLSelectElement} */
658
- const fileTransferDirectionSelect = document.getElementById("fileTransferDirection");
795
+ const fileTransferDirectionSelect = document.getElementById(
796
+ "fileTransferDirection"
797
+ );
659
798
  fileTransferDirectionSelect.addEventListener("input", () => {
660
799
  fileTransferDirection = fileTransferDirectionSelect.value;
661
800
  console.log({ fileTransferDirection });
@@ -759,9 +898,13 @@ device.addEventListener("getTfliteInferencingEnabled", () => {
759
898
  /** @type {HTMLSpanElement} */
760
899
  const tfliteSampleRateSpan = document.getElementById("tfliteSampleRate");
761
900
  /** @type {HTMLInputElement} */
762
- const setTfliteSampleRateInput = document.getElementById("setTfliteSampleRateInput");
901
+ const setTfliteSampleRateInput = document.getElementById(
902
+ "setTfliteSampleRateInput"
903
+ );
763
904
  /** @type {HTMLButtonElement} */
764
- const setTfliteSampleRateButton = document.getElementById("setTfliteSampleRateButton");
905
+ const setTfliteSampleRateButton = document.getElementById(
906
+ "setTfliteSampleRateButton"
907
+ );
765
908
 
766
909
  device.addEventListener("isConnected", () => {
767
910
  const disabled = !device.isConnected;
@@ -793,16 +936,22 @@ device.addEventListener("getTfliteInferencingEnabled", () => {
793
936
 
794
937
  const tfliteSensorTypesContainer = document.getElementById("tfliteSensorTypes");
795
938
  /** @type {HTMLTemplateElement} */
796
- const tfliteSensorTypeTemplate = document.getElementById("tfliteSensorTypeTemplate");
939
+ const tfliteSensorTypeTemplate = document.getElementById(
940
+ "tfliteSensorTypeTemplate"
941
+ );
797
942
  /** @type {Object.<string, HTMLElement>} */
798
943
  const tfliteSensorTypeContainers = {};
799
944
  /** @type {BS.SensorType[]} */
800
945
  let tfliteSensorTypes = [];
801
946
  /** @type {HTMLButtonElement} */
802
- const setTfliteSensorTypesButton = document.getElementById("setTfliteSensorTypes");
947
+ const setTfliteSensorTypesButton = document.getElementById(
948
+ "setTfliteSensorTypes"
949
+ );
803
950
 
804
951
  BS.TfliteSensorTypes.forEach((sensorType) => {
805
- const sensorTypeContainer = tfliteSensorTypeTemplate.content.cloneNode(true).querySelector(".sensorType");
952
+ const sensorTypeContainer = tfliteSensorTypeTemplate.content
953
+ .cloneNode(true)
954
+ .querySelector(".sensorType");
806
955
  sensorTypeContainer.querySelector(".name").innerText = sensorType;
807
956
 
808
957
  /** @type {HTMLInputElement} */
@@ -817,7 +966,8 @@ BS.TfliteSensorTypes.forEach((sensorType) => {
817
966
  });
818
967
 
819
968
  device.addEventListener("getTfliteSensorTypes", () => {
820
- isSensorEnabledInput.checked = device.tfliteSensorTypes.includes(sensorType);
969
+ isSensorEnabledInput.checked =
970
+ device.tfliteSensorTypes.includes(sensorType);
821
971
  });
822
972
  isSensorEnabledInput.checked = device.tfliteSensorTypes.includes(sensorType);
823
973
 
@@ -852,9 +1002,13 @@ device.addEventListener("tfliteIsReady", () => {
852
1002
  /** @type {HTMLSpanElement} */
853
1003
  const tfliteThresholdSpan = document.getElementById("tfliteThreshold");
854
1004
  /** @type {HTMLInputElement} */
855
- const setTfliteThresholdInput = document.getElementById("setTfliteThresholdInput");
1005
+ const setTfliteThresholdInput = document.getElementById(
1006
+ "setTfliteThresholdInput"
1007
+ );
856
1008
  /** @type {HTMLButtonElement} */
857
- const setTfliteThresholdButton = document.getElementById("setTfliteThresholdButton");
1009
+ const setTfliteThresholdButton = document.getElementById(
1010
+ "setTfliteThresholdButton"
1011
+ );
858
1012
 
859
1013
  device.addEventListener("isConnected", () => {
860
1014
  const disabled = !device.isConnected;
@@ -884,9 +1038,13 @@ setTfliteThresholdButton.addEventListener("click", () => {
884
1038
  /** @type {HTMLSpanElement} */
885
1039
  const tfliteCaptureDelaySpan = document.getElementById("tfliteCaptureDelay");
886
1040
  /** @type {HTMLInputElement} */
887
- const setTfliteCaptureDelayInput = document.getElementById("setTfliteCaptureDelayInput");
1041
+ const setTfliteCaptureDelayInput = document.getElementById(
1042
+ "setTfliteCaptureDelayInput"
1043
+ );
888
1044
  /** @type {HTMLButtonElement} */
889
- const setTfliteCaptureDelayButton = document.getElementById("setTfliteCaptureDelayButton");
1045
+ const setTfliteCaptureDelayButton = document.getElementById(
1046
+ "setTfliteCaptureDelayButton"
1047
+ );
890
1048
 
891
1049
  device.addEventListener("isConnected", () => {
892
1050
  const disabled = !device.isConnected;
@@ -914,18 +1072,23 @@ setTfliteCaptureDelayButton.addEventListener("click", () => {
914
1072
  });
915
1073
 
916
1074
  /** @type {HTMLInputElement} */
917
- const tfliteInferencingEnabledInput = document.getElementById("tfliteInferencingEnabled");
1075
+ const tfliteInferencingEnabledInput = document.getElementById(
1076
+ "tfliteInferencingEnabled"
1077
+ );
918
1078
  /** @type {HTMLButtonElement} */
919
- const toggleTfliteInferencingEnabledButton = document.getElementById("toggleTfliteInferencingEnabled");
1079
+ const toggleTfliteInferencingEnabledButton = document.getElementById(
1080
+ "toggleTfliteInferencingEnabled"
1081
+ );
920
1082
 
921
1083
  device.addEventListener("tfliteIsReady", () => {
922
1084
  toggleTfliteInferencingEnabledButton.disabled = !device.tfliteIsReady;
923
1085
  });
924
1086
  device.addEventListener("getTfliteInferencingEnabled", () => {
925
1087
  tfliteInferencingEnabledInput.checked = device.tfliteInferencingEnabled;
926
- toggleTfliteInferencingEnabledButton.innerText = device.tfliteInferencingEnabled
927
- ? "disable inferencing"
928
- : "enable inferencing";
1088
+ toggleTfliteInferencingEnabledButton.innerText =
1089
+ device.tfliteInferencingEnabled
1090
+ ? "disable inferencing"
1091
+ : "enable inferencing";
929
1092
  });
930
1093
 
931
1094
  toggleTfliteInferencingEnabledButton.addEventListener("click", () => {
@@ -958,7 +1121,8 @@ device.addEventListener("tfliteInference", (event) => {
958
1121
  tfliteInferencePre.textContent = JSON.stringify(tfliteInference, null, 2);
959
1122
 
960
1123
  if (device.tfliteTask == "classification") {
961
- topInferenceClassElement.innerText = inferenceClasses[tfliteInference.maxIndex - 1] ?? "";
1124
+ topInferenceClassElement.innerText =
1125
+ inferenceClasses[tfliteInference.maxIndex - 1] ?? "";
962
1126
  }
963
1127
  });
964
1128
 
@@ -974,7 +1138,9 @@ firmwareInput.addEventListener("input", () => {
974
1138
  updateToggleFirmwareUploadButton();
975
1139
  });
976
1140
  /** @type {HTMLButtonElement} */
977
- const toggleFirmwareUploadButton = document.getElementById("toggleFirmwareUpload");
1141
+ const toggleFirmwareUploadButton = document.getElementById(
1142
+ "toggleFirmwareUpload"
1143
+ );
978
1144
  toggleFirmwareUploadButton.addEventListener("click", () => {
979
1145
  device.uploadFirmware(firmware);
980
1146
  });
@@ -987,20 +1153,28 @@ device.addEventListener("isConnected", () => {
987
1153
  });
988
1154
 
989
1155
  /** @type {HTMLProgressElement} */
990
- const firmwareUploadProgress = document.getElementById("firmwareUploadProgress");
1156
+ const firmwareUploadProgress = document.getElementById(
1157
+ "firmwareUploadProgress"
1158
+ );
991
1159
  /** @type {HTMLSpanElement} */
992
- const firmwareUploadProgressPercentageSpan = document.getElementById("firmwareUploadProgressPercentage");
1160
+ const firmwareUploadProgressPercentageSpan = document.getElementById(
1161
+ "firmwareUploadProgressPercentage"
1162
+ );
993
1163
  device.addEventListener("firmwareUploadProgress", (event) => {
994
1164
  const progress = event.message.progress;
995
1165
  firmwareUploadProgress.value = progress;
996
- firmwareUploadProgressPercentageSpan.innerText = `${Math.floor(100 * progress)}%`;
1166
+ firmwareUploadProgressPercentageSpan.innerText = `${Math.floor(
1167
+ 100 * progress
1168
+ )}%`;
997
1169
  });
998
1170
  device.addEventListener("firmwareUploadComplete", () => {
999
1171
  firmwareUploadProgress.value = 0;
1000
1172
  });
1001
1173
  device.addEventListener("firmwareStatus", () => {
1002
1174
  const isUploading = device.firmwareStatus == "uploading";
1003
- firmwareUploadProgressPercentageSpan.style.display = isUploading ? "" : "none";
1175
+ firmwareUploadProgressPercentageSpan.style.display = isUploading
1176
+ ? ""
1177
+ : "none";
1004
1178
  });
1005
1179
 
1006
1180
  /** @type {HTMLPreElement} */
@@ -1040,7 +1214,7 @@ resetButton.addEventListener("click", () => {
1040
1214
  const updateResetButton = () => {
1041
1215
  const status = device.firmwareStatus;
1042
1216
  const enabled = status == "pending" || status == "testing";
1043
- resetButton.disabled = !enabled;
1217
+ resetButton.disabled = !enabled || !device.canReset;
1044
1218
  };
1045
1219
 
1046
1220
  /** @type {HTMLButtonElement} */
@@ -1054,12 +1228,15 @@ const updateTestFirmwareImageButton = () => {
1054
1228
  };
1055
1229
 
1056
1230
  /** @type {HTMLButtonElement} */
1057
- const confirmFirmwareImageButton = document.getElementById("confirmFirmwareImage");
1231
+ const confirmFirmwareImageButton = document.getElementById(
1232
+ "confirmFirmwareImage"
1233
+ );
1058
1234
  confirmFirmwareImageButton.addEventListener("click", () => {
1059
1235
  device.confirmFirmwareImage(selectedImageIndex);
1060
1236
  });
1061
1237
  const updateConfirmFirmwareImageButton = () => {
1062
- const enabled = device.firmwareStatus == "testing" || device.firmwareStatus == "uploaded";
1238
+ const enabled =
1239
+ device.firmwareStatus == "testing" || device.firmwareStatus == "uploaded";
1063
1240
  confirmFirmwareImageButton.disabled = !enabled;
1064
1241
  };
1065
1242
 
@@ -1080,7 +1257,10 @@ const imageSelectionOptGroup = imageSelectionSelect.querySelector("optgroup");
1080
1257
  device.addEventListener("firmwareImages", () => {
1081
1258
  imageSelectionOptGroup.innerHTML = "";
1082
1259
  device.firmwareImages.forEach((firmwareImage, index) => {
1083
- const option = new Option(`${firmwareImage.version} (slot ${index})`, index);
1260
+ const option = new Option(
1261
+ `${firmwareImage.version} (slot ${index})`,
1262
+ index
1263
+ );
1084
1264
  option.disabled = firmwareImage.empty;
1085
1265
  imageSelectionOptGroup.appendChild(option);
1086
1266
  });
@@ -1105,3 +1285,676 @@ function updateSelectImageSelect() {
1105
1285
  }
1106
1286
  imageSelectionSelect.disabled = !enabled;
1107
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
+ /** @type {HTMLInputElement} */
1658
+ const cameraWhiteBalanceInput = document.getElementById("cameraWhiteBalance");
1659
+ const updateWhiteBalance = BS.ThrottleUtils.throttle(
1660
+ (config) => {
1661
+ if (device.cameraStatus != "idle") {
1662
+ return;
1663
+ }
1664
+
1665
+ device.setCameraConfiguration(config);
1666
+
1667
+ if (takePictureAfterUpdate) {
1668
+ device.addEventListener(
1669
+ "getCameraConfiguration",
1670
+ () => {
1671
+ setTimeout(() => device.takePicture()), 100;
1672
+ },
1673
+ { once: true }
1674
+ );
1675
+ }
1676
+ },
1677
+ 200,
1678
+ true
1679
+ );
1680
+ cameraWhiteBalanceInput.addEventListener("input", () => {
1681
+ let [redGain, greenGain, blueGain] = cameraWhiteBalanceInput.value
1682
+ .replace("#", "")
1683
+ .match(/.{1,2}/g)
1684
+ .map((value) => Number(`0x${value}`))
1685
+ .map((value) => value / 255)
1686
+ .map((value) => value * device.cameraConfigurationRanges.blueGain.max)
1687
+ .map((value) => Math.round(value));
1688
+
1689
+ updateWhiteBalance({ redGain, greenGain, blueGain });
1690
+ });
1691
+ const updateCameraWhiteBalanceInput = () => {
1692
+ if (!device.hasCamera) {
1693
+ return;
1694
+ }
1695
+ cameraWhiteBalanceInput.disabled =
1696
+ !device.isConnected || !device.hasCamera || device.cameraStatus != "idle";
1697
+
1698
+ const { redGain, blueGain, greenGain } = device.cameraConfiguration;
1699
+
1700
+ cameraWhiteBalanceInput.value = `#${[redGain, blueGain, greenGain]
1701
+ .map((value) => value / device.cameraConfigurationRanges.redGain.max)
1702
+ .map((value) => value * 255)
1703
+ .map((value) => Math.round(value))
1704
+ .map((value) => value.toString(16))
1705
+ .join("")}`;
1706
+ };
1707
+ device.addEventListener("connected", () => {
1708
+ updateCameraWhiteBalanceInput();
1709
+ });
1710
+ device.addEventListener("getCameraConfiguration", () => {
1711
+ updateCameraWhiteBalanceInput();
1712
+ });
1713
+
1714
+ // MICROPHONE
1715
+
1716
+ /** @type {HTMLSpanElement} */
1717
+ const isMicrophoneAvailableSpan = document.getElementById(
1718
+ "isMicrophoneAvailable"
1719
+ );
1720
+ device.addEventListener("connected", () => {
1721
+ isMicrophoneAvailableSpan.innerText = device.hasMicrophone;
1722
+ });
1723
+
1724
+ /** @type {HTMLSpanElement} */
1725
+ const microphoneStatusSpan = document.getElementById("microphoneStatus");
1726
+ device.addEventListener("microphoneStatus", () => {
1727
+ microphoneStatusSpan.innerText = device.microphoneStatus;
1728
+ });
1729
+
1730
+ /** @type {HTMLPreElement} */
1731
+ const microphoneConfigurationPre = document.getElementById(
1732
+ "microphoneConfigurationPre"
1733
+ );
1734
+ device.addEventListener("getMicrophoneConfiguration", () => {
1735
+ microphoneConfigurationPre.textContent = JSON.stringify(
1736
+ device.microphoneConfiguration,
1737
+ null,
1738
+ 2
1739
+ );
1740
+ });
1741
+
1742
+ const microphoneConfigurationContainer = document.getElementById(
1743
+ "microphoneConfiguration"
1744
+ );
1745
+ /** @type {HTMLTemplateElement} */
1746
+ const microphoneConfigurationTypeTemplate = document.getElementById(
1747
+ "microphoneConfigurationTypeTemplate"
1748
+ );
1749
+ BS.MicrophoneConfigurationTypes.forEach((microphoneConfigurationType) => {
1750
+ const microphoneConfigurationTypeContainer =
1751
+ microphoneConfigurationTypeTemplate.content
1752
+ .cloneNode(true)
1753
+ .querySelector(".microphoneConfigurationType");
1754
+
1755
+ microphoneConfigurationContainer.appendChild(
1756
+ microphoneConfigurationTypeContainer
1757
+ );
1758
+
1759
+ microphoneConfigurationTypeContainer.querySelector(".type").innerText =
1760
+ microphoneConfigurationType;
1761
+
1762
+ /** @type {HTMLSelectElement} */
1763
+ const select = microphoneConfigurationTypeContainer.querySelector("select");
1764
+ /** @type {HTMLOptGroupElement} */
1765
+ const optgroup = select.querySelector("optgroup");
1766
+ optgroup.label = microphoneConfigurationType;
1767
+
1768
+ BS.MicrophoneConfigurationValues[microphoneConfigurationType].forEach(
1769
+ (value) => {
1770
+ optgroup.appendChild(new Option(value));
1771
+ }
1772
+ );
1773
+
1774
+ /** @type {HTMLSpanElement} */
1775
+ const span = microphoneConfigurationTypeContainer.querySelector("span");
1776
+
1777
+ device.addEventListener("isConnected", () => {
1778
+ updateisInputDisabled();
1779
+ });
1780
+ device.addEventListener("microphoneStatus", () => {
1781
+ updateisInputDisabled();
1782
+ });
1783
+ const updateisInputDisabled = () => {
1784
+ select.disabled =
1785
+ !device.isConnected ||
1786
+ !device.hasMicrophone ||
1787
+ device.microphoneStatus != "idle";
1788
+ };
1789
+
1790
+ const updateSelect = () => {
1791
+ const value = device.microphoneConfiguration[microphoneConfigurationType];
1792
+ span.innerText = value;
1793
+ select.value = value;
1794
+ };
1795
+
1796
+ device.addEventListener("connected", () => {
1797
+ if (!device.hasMicrophone) {
1798
+ return;
1799
+ }
1800
+ updateSelect();
1801
+ });
1802
+
1803
+ device.addEventListener("getMicrophoneConfiguration", () => {
1804
+ updateSelect();
1805
+ });
1806
+
1807
+ select.addEventListener("input", () => {
1808
+ const value = select.value;
1809
+ // console.log(`updating ${microphoneConfigurationType} to ${value}`);
1810
+ device.setMicrophoneConfiguration({
1811
+ [microphoneConfigurationType]: value,
1812
+ });
1813
+ });
1814
+ });
1815
+
1816
+ /** @type {HTMLButtonElement} */
1817
+ const toggleMicrophoneButton = document.getElementById("toggleMicrophone");
1818
+ toggleMicrophoneButton.addEventListener("click", () => {
1819
+ device.toggleMicrophone();
1820
+ });
1821
+ device.addEventListener("connected", () => {
1822
+ updateToggleMicrophoneButton();
1823
+ });
1824
+ device.addEventListener("getSensorConfiguration", () => {
1825
+ updateToggleMicrophoneButton();
1826
+ });
1827
+ const updateToggleMicrophoneButton = () => {
1828
+ let disabled =
1829
+ !device.isConnected ||
1830
+ device.sensorConfiguration.microphone == 0 ||
1831
+ !device.hasMicrophone;
1832
+
1833
+ switch (device.microphoneStatus) {
1834
+ case "streaming":
1835
+ toggleMicrophoneButton.innerText = "stop microphone";
1836
+ break;
1837
+ case "idle":
1838
+ toggleMicrophoneButton.innerText = "start microphone";
1839
+ break;
1840
+ }
1841
+ toggleMicrophoneButton.disabled = disabled;
1842
+ };
1843
+ device.addEventListener("microphoneStatus", () => {
1844
+ updateToggleMicrophoneButton();
1845
+ });
1846
+
1847
+ /** @type {HTMLButtonElement} */
1848
+ const startMicrophoneButton = document.getElementById("startMicrophone");
1849
+ startMicrophoneButton.addEventListener("click", () => {
1850
+ device.startMicrophone();
1851
+ });
1852
+ /** @type {HTMLButtonElement} */
1853
+ const stopMicrophoneButton = document.getElementById("stopMicrophone");
1854
+ stopMicrophoneButton.addEventListener("click", () => {
1855
+ device.stopMicrophone();
1856
+ });
1857
+ /** @type {HTMLButtonElement} */
1858
+ const enableMicrophoneVadButton = document.getElementById("enableMicrphoneVad");
1859
+ enableMicrophoneVadButton.addEventListener("click", () => {
1860
+ device.enableMicrophoneVad();
1861
+ });
1862
+
1863
+ const updateMicrophoneButtons = () => {
1864
+ let disabled =
1865
+ !device.isConnected ||
1866
+ device.sensorConfiguration.microphone == 0 ||
1867
+ !device.hasMicrophone;
1868
+
1869
+ startMicrophoneButton.disabled =
1870
+ disabled || device.microphoneStatus == "streaming";
1871
+ stopMicrophoneButton.disabled = disabled || device.microphoneStatus == "idle";
1872
+ enableMicrophoneVadButton.disabled =
1873
+ disabled || device.microphoneStatus == "vad";
1874
+ };
1875
+ device.addEventListener("microphoneStatus", () => {
1876
+ updateMicrophoneButtons();
1877
+ });
1878
+ device.addEventListener("connected", () => {
1879
+ updateMicrophoneButtons();
1880
+ });
1881
+ device.addEventListener("getSensorConfiguration", () => {
1882
+ updateMicrophoneButtons();
1883
+ });
1884
+
1885
+ const audioContext = new (window.AudioContext || window.webkitAudioContext)();
1886
+ const checkAudioContextState = () => {
1887
+ const { state } = audioContext;
1888
+ console.log({ audioContextState: state });
1889
+ if (state != "running") {
1890
+ document.addEventListener("click", () => audioContext.resume(), {
1891
+ once: true,
1892
+ });
1893
+ }
1894
+ };
1895
+ audioContext.addEventListener("statechange", () => {
1896
+ checkAudioContextState();
1897
+ });
1898
+ checkAudioContextState();
1899
+
1900
+ device.audioContext = audioContext;
1901
+
1902
+ /** @type {HTMLAudioElement} */
1903
+ const microphoneStreamAudioElement =
1904
+ document.getElementById("microphoneStream");
1905
+ microphoneStreamAudioElement.srcObject =
1906
+ device.microphoneMediaStreamDestination.stream;
1907
+
1908
+ /** @type {HTMLAudioElement} */
1909
+ const microphoneRecordingAudioElement = document.getElementById(
1910
+ "microphoneRecording"
1911
+ );
1912
+ /** @type {HTMLInputElement} */
1913
+ const autoPlayMicrphoneRecordingCheckbox = document.getElementById(
1914
+ "autoPlayMicrphoneRecording"
1915
+ );
1916
+ let autoPlayMicrphoneRecording = autoPlayMicrphoneRecordingCheckbox.checked;
1917
+ console.log("autoPlayMicrphoneRecording", autoPlayMicrphoneRecording);
1918
+ autoPlayMicrphoneRecordingCheckbox.addEventListener("input", () => {
1919
+ autoPlayMicrphoneRecording = autoPlayMicrphoneRecordingCheckbox.checked;
1920
+ console.log({ autoPlayMicrphoneRecording });
1921
+ });
1922
+ device.addEventListener("microphoneRecording", (event) => {
1923
+ microphoneRecordingAudioElement.src = event.message.url;
1924
+ if (autoPlayMicrphoneRecording) {
1925
+ microphoneRecordingAudioElement.play();
1926
+ }
1927
+ });
1928
+
1929
+ /** @type {HTMLButtonElement} */
1930
+ const toggleMicrophoneRecordingButton = document.getElementById(
1931
+ "toggleMicrophoneRecording"
1932
+ );
1933
+ toggleMicrophoneRecordingButton.addEventListener("click", () => {
1934
+ device.toggleMicrophoneRecording();
1935
+ });
1936
+ device.addEventListener("connected", () => {
1937
+ updateToggleMicrophoneRecordingButton();
1938
+ });
1939
+ device.addEventListener("getSensorConfiguration", () => {
1940
+ updateToggleMicrophoneRecordingButton();
1941
+ });
1942
+ const updateToggleMicrophoneRecordingButton = () => {
1943
+ let disabled =
1944
+ !device.isConnected ||
1945
+ device.sensorConfiguration.microphone == 0 ||
1946
+ !device.hasMicrophone ||
1947
+ device.microphoneStatus != "streaming";
1948
+
1949
+ toggleMicrophoneRecordingButton.innerText = device.isRecordingMicrophone
1950
+ ? "stop recording"
1951
+ : "start recording";
1952
+
1953
+ toggleMicrophoneRecordingButton.disabled = disabled;
1954
+ };
1955
+ device.addEventListener("isRecordingMicrophone", () => {
1956
+ updateToggleMicrophoneRecordingButton();
1957
+ });
1958
+ device.addEventListener("microphoneStatus", () => {
1959
+ updateToggleMicrophoneRecordingButton();
1960
+ });