brilliantsole 0.0.1

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 (158) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +21 -0
  3. package/build/brilliantsole.cjs +5957 -0
  4. package/build/brilliantsole.cjs.map +1 -0
  5. package/build/brilliantsole.js +5448 -0
  6. package/build/brilliantsole.js.map +1 -0
  7. package/build/brilliantsole.ls.js +4872 -0
  8. package/build/brilliantsole.ls.js.map +1 -0
  9. package/build/brilliantsole.min.js +6 -0
  10. package/build/brilliantsole.min.js.map +1 -0
  11. package/build/brilliantsole.module.d.ts +908 -0
  12. package/build/brilliantsole.module.js +5412 -0
  13. package/build/brilliantsole.module.js.map +1 -0
  14. package/build/brilliantsole.module.min.d.ts +908 -0
  15. package/build/brilliantsole.module.min.js +6 -0
  16. package/build/brilliantsole.module.min.js.map +1 -0
  17. package/build/brilliantsole.node.module.d.ts +908 -0
  18. package/build/brilliantsole.node.module.js +5906 -0
  19. package/build/brilliantsole.node.module.js.map +1 -0
  20. package/build/dts/BS.d.ts +25 -0
  21. package/build/dts/Device.d.ts +136 -0
  22. package/build/dts/DeviceInformationManager.d.ts +56 -0
  23. package/build/dts/DeviceManager.d.ts +67 -0
  24. package/build/dts/FileTransferManager.d.ts +84 -0
  25. package/build/dts/FirmwareManager.d.ts +71 -0
  26. package/build/dts/InformationManager.d.ts +66 -0
  27. package/build/dts/TfliteManager.d.ts +92 -0
  28. package/build/dts/connection/BaseConnectionManager.d.ts +59 -0
  29. package/build/dts/connection/ClientConnectionManager.d.ts +23 -0
  30. package/build/dts/connection/WebSocketClientConnectionManager.d.ts +23 -0
  31. package/build/dts/connection/bluetooth/BluetoothConnectionManager.d.ts +10 -0
  32. package/build/dts/connection/bluetooth/NobleConnectionManager.d.ts +42 -0
  33. package/build/dts/connection/bluetooth/WebBluetoothConnectionManager.d.ts +20 -0
  34. package/build/dts/connection/bluetooth/bluetoothUUIDs.d.ts +14 -0
  35. package/build/dts/connection/webSocket/ClientConnectionManager.d.ts +23 -0
  36. package/build/dts/connection/webSocket/WebSocketClientConnectionManager.d.ts +23 -0
  37. package/build/dts/devicePair/DevicePair.d.ts +60 -0
  38. package/build/dts/devicePair/DevicePairPressureSensorDataManager.d.ts +25 -0
  39. package/build/dts/devicePair/DevicePairSensorDataManager.d.ts +33 -0
  40. package/build/dts/scanner/BaseScanner.d.ts +66 -0
  41. package/build/dts/scanner/NobleScanner.d.ts +17 -0
  42. package/build/dts/scanner/Scanner.d.ts +3 -0
  43. package/build/dts/sensor/BarometerSensorDataManager.d.ts +16 -0
  44. package/build/dts/sensor/MotionSensorDataManager.d.ts +69 -0
  45. package/build/dts/sensor/PressureSensorDataManager.d.ts +36 -0
  46. package/build/dts/sensor/SensorConfigurationManager.d.ts +44 -0
  47. package/build/dts/sensor/SensorDataManager.d.ts +40 -0
  48. package/build/dts/server/BaseClient.d.ts +85 -0
  49. package/build/dts/server/BaseServer.d.ts +48 -0
  50. package/build/dts/server/ServerUtils.d.ts +23 -0
  51. package/build/dts/server/udp/UDPServer.d.ts +11 -0
  52. package/build/dts/server/udp/UDPUtils.d.ts +9 -0
  53. package/build/dts/server/websocket/WebSocketClient.d.ts +17 -0
  54. package/build/dts/server/websocket/WebSocketServer.d.ts +13 -0
  55. package/build/dts/server/websocket/WebSocketUtils.d.ts +9 -0
  56. package/build/dts/utils/ArrayBufferUtils.d.ts +7 -0
  57. package/build/dts/utils/ArrayUtils.d.ts +2 -0
  58. package/build/dts/utils/CenterOfPressureHelper.d.ts +15 -0
  59. package/build/dts/utils/Console.d.ts +34 -0
  60. package/build/dts/utils/EventDispatcher.d.ts +50 -0
  61. package/build/dts/utils/EventUtils.d.ts +6 -0
  62. package/build/dts/utils/MathUtils.d.ts +21 -0
  63. package/build/dts/utils/ParseUtils.d.ts +5 -0
  64. package/build/dts/utils/RangeHelper.d.ts +8 -0
  65. package/build/dts/utils/Text.d.ts +6 -0
  66. package/build/dts/utils/Timer.d.ts +14 -0
  67. package/build/dts/utils/TypeScriptUtils.d.ts +19 -0
  68. package/build/dts/utils/cbor.d.ts +6 -0
  69. package/build/dts/utils/checksum.d.ts +3 -0
  70. package/build/dts/utils/environment.d.ts +13 -0
  71. package/build/dts/utils/mcumgr.d.ts +88 -0
  72. package/build/dts/utils/stringUtils.d.ts +2 -0
  73. package/build/dts/vibration/VibrationManager.d.ts +45 -0
  74. package/build/dts/vibration/VibrationWaveformEffects.d.ts +2 -0
  75. package/build/index.d.ts +908 -0
  76. package/build/index.node.d.ts +908 -0
  77. package/examples/3d/index.html +109 -0
  78. package/examples/3d/scene.html +57 -0
  79. package/examples/3d/script.js +419 -0
  80. package/examples/balance/index.html +138 -0
  81. package/examples/balance/script.js +243 -0
  82. package/examples/basic/index.html +327 -0
  83. package/examples/basic/script.js +1093 -0
  84. package/examples/center-of-pressure/index.html +132 -0
  85. package/examples/center-of-pressure/script.js +207 -0
  86. package/examples/device-pair/index.html +72 -0
  87. package/examples/device-pair/script.js +187 -0
  88. package/examples/edge-impulse/index.html +94 -0
  89. package/examples/edge-impulse/script.js +1033 -0
  90. package/examples/graph/index.html +83 -0
  91. package/examples/graph/script.js +469 -0
  92. package/examples/machine-learning/index.html +366 -0
  93. package/examples/machine-learning/script.js +1774 -0
  94. package/examples/pressure/index.html +145 -0
  95. package/examples/pressure/script.js +201 -0
  96. package/examples/recording/index.html +187 -0
  97. package/examples/recording/script.js +736 -0
  98. package/examples/server/index.html +266 -0
  99. package/examples/server/script.js +925 -0
  100. package/examples/utils/aframe/fingertip-button-component.js +201 -0
  101. package/examples/utils/aframe/fingertip-collider-target-component.js +102 -0
  102. package/examples/utils/aframe/fingertip-colliders-component.js +147 -0
  103. package/examples/utils/three/three.module.min.js +24846 -0
  104. package/examples/webxr/index.html +221 -0
  105. package/examples/webxr/script.js +1127 -0
  106. package/package.json +83 -0
  107. package/src/BS.ts +68 -0
  108. package/src/Device.ts +734 -0
  109. package/src/DeviceInformationManager.ts +146 -0
  110. package/src/DeviceManager.ts +354 -0
  111. package/src/FileTransferManager.ts +452 -0
  112. package/src/FirmwareManager.ts +357 -0
  113. package/src/InformationManager.ts +283 -0
  114. package/src/TfliteManager.ts +450 -0
  115. package/src/connection/BaseConnectionManager.ts +255 -0
  116. package/src/connection/ClientConnectionManager.ts +120 -0
  117. package/src/connection/bluetooth/BluetoothConnectionManager.ts +34 -0
  118. package/src/connection/bluetooth/NobleConnectionManager.ts +302 -0
  119. package/src/connection/bluetooth/WebBluetoothConnectionManager.ts +269 -0
  120. package/src/connection/bluetooth/bluetoothUUIDs.ts +218 -0
  121. package/src/devicePair/DevicePair.ts +253 -0
  122. package/src/devicePair/DevicePairPressureSensorDataManager.ts +82 -0
  123. package/src/devicePair/DevicePairSensorDataManager.ts +90 -0
  124. package/src/scanner/BaseScanner.ts +189 -0
  125. package/src/scanner/NobleScanner.ts +195 -0
  126. package/src/scanner/Scanner.ts +16 -0
  127. package/src/sensor/BarometerSensorDataManager.ts +41 -0
  128. package/src/sensor/MotionSensorDataManager.ts +151 -0
  129. package/src/sensor/PressureSensorDataManager.ts +112 -0
  130. package/src/sensor/SensorConfigurationManager.ts +177 -0
  131. package/src/sensor/SensorDataManager.ts +166 -0
  132. package/src/server/BaseClient.ts +368 -0
  133. package/src/server/BaseServer.ts +344 -0
  134. package/src/server/ServerUtils.ts +93 -0
  135. package/src/server/udp/UDPServer.ts +229 -0
  136. package/src/server/udp/UDPUtils.ts +20 -0
  137. package/src/server/websocket/WebSocketClient.ts +179 -0
  138. package/src/server/websocket/WebSocketServer.ts +184 -0
  139. package/src/server/websocket/WebSocketUtils.ts +20 -0
  140. package/src/utils/ArrayBufferUtils.ts +88 -0
  141. package/src/utils/ArrayUtils.ts +15 -0
  142. package/src/utils/CenterOfPressureHelper.ts +39 -0
  143. package/src/utils/Console.ts +156 -0
  144. package/src/utils/EventDispatcher.ts +153 -0
  145. package/src/utils/EventUtils.ts +41 -0
  146. package/src/utils/MathUtils.ts +53 -0
  147. package/src/utils/ParseUtils.ts +46 -0
  148. package/src/utils/RangeHelper.ts +38 -0
  149. package/src/utils/Text.ts +30 -0
  150. package/src/utils/Timer.ts +72 -0
  151. package/src/utils/TypeScriptUtils.ts +22 -0
  152. package/src/utils/cbor.js +429 -0
  153. package/src/utils/checksum.ts +41 -0
  154. package/src/utils/environment.ts +46 -0
  155. package/src/utils/mcumgr.js +444 -0
  156. package/src/utils/stringUtils.ts +11 -0
  157. package/src/vibration/VibrationManager.ts +308 -0
  158. package/src/vibration/VibrationWaveformEffects.ts +128 -0
@@ -0,0 +1,1093 @@
1
+ import * as BS from "../../build/brilliantsole.module.js";
2
+ window.BS = BS;
3
+ console.log(BS);
4
+
5
+ const device = new BS.Device();
6
+ console.log({ device });
7
+ window.device = device;
8
+
9
+ //BS.setAllConsoleLevelFlags({ log: false });
10
+ //BS.setConsoleLevelFlagsForType("PressureDataManager", { log: true });
11
+
12
+ // GET DEVICES
13
+ /** @type {HTMLTemplateElement} */
14
+ const availableDeviceTemplate = document.getElementById("availableDeviceTemplate");
15
+ const availableDevicesContainer = document.getElementById("availableDevices");
16
+ /** @param {BS.Device[]} availableDevices */
17
+ function onAvailableDevices(availableDevices) {
18
+ availableDevicesContainer.innerHTML = "";
19
+ if (availableDevices.length == 0) {
20
+ availableDevicesContainer.innerText = "no devices available";
21
+ } else {
22
+ availableDevices.forEach((availableDevice) => {
23
+ const availableDeviceContainer = availableDeviceTemplate.content
24
+ .cloneNode(true)
25
+ .querySelector(".availableDevice");
26
+ availableDeviceContainer.querySelector(".name").innerText = availableDevice.name;
27
+ availableDeviceContainer.querySelector(".type").innerText = availableDevice.type;
28
+
29
+ /** @type {HTMLButtonElement} */
30
+ const toggleConnectionButton = availableDeviceContainer.querySelector(".toggleConnection");
31
+ toggleConnectionButton.addEventListener("click", () => {
32
+ device.connectionManager = availableDevice.connectionManager;
33
+ device.reconnect();
34
+ });
35
+ device.addEventListener("connectionStatus", () => {
36
+ toggleConnectionButton.disabled = device.connectionStatus != "notConnected";
37
+ });
38
+ toggleConnectionButton.disabled = device.connectionStatus != "notConnected";
39
+
40
+ availableDevicesContainer.appendChild(availableDeviceContainer);
41
+ });
42
+ }
43
+ }
44
+ async function getDevices() {
45
+ const availableDevices = await BS.DeviceManager.GetDevices();
46
+ if (!availableDevices) {
47
+ return;
48
+ }
49
+ onAvailableDevices(availableDevices);
50
+ }
51
+
52
+ BS.DeviceManager.AddEventListener("availableDevices", (event) => {
53
+ const devices = event.message.availableDevices;
54
+ onAvailableDevices(devices);
55
+ });
56
+ getDevices();
57
+
58
+ // CONNECTION
59
+
60
+ /** @type {HTMLButtonElement} */
61
+ const toggleConnectionButton = document.getElementById("toggleConnection");
62
+ toggleConnectionButton.addEventListener("click", () => {
63
+ switch (device.connectionStatus) {
64
+ case "notConnected":
65
+ device.connect();
66
+ break;
67
+ case "connected":
68
+ device.disconnect();
69
+ break;
70
+ }
71
+ });
72
+
73
+ /** @type {HTMLButtonElement} */
74
+ const reconnectButton = document.getElementById("reconnect");
75
+ reconnectButton.addEventListener("click", () => {
76
+ device.reconnect();
77
+ });
78
+ device.addEventListener("connectionStatus", () => {
79
+ reconnectButton.disabled = !device.canReconnect;
80
+ });
81
+
82
+ device.addEventListener("connectionStatus", () => {
83
+ switch (device.connectionStatus) {
84
+ case "connected":
85
+ case "notConnected":
86
+ toggleConnectionButton.disabled = false;
87
+ toggleConnectionButton.innerText = device.isConnected ? "disconnect" : "connect";
88
+ break;
89
+ case "connecting":
90
+ case "disconnecting":
91
+ toggleConnectionButton.disabled = true;
92
+ toggleConnectionButton.innerText = device.connectionStatus;
93
+ break;
94
+ }
95
+ });
96
+
97
+ /** @type {HTMLInputElement} */
98
+ const reconnectOnDisconnectionCheckbox = document.getElementById("reconnectOnDisconnection");
99
+ reconnectOnDisconnectionCheckbox.addEventListener("input", () => {
100
+ device.reconnectOnDisconnection = reconnectOnDisconnectionCheckbox.checked;
101
+ });
102
+
103
+ /** @type {HTMLButtonElement} */
104
+ const resetDeviceButton = document.getElementById("resetDevice");
105
+ device.addEventListener("isConnected", () => {
106
+ resetDeviceButton.disabled = !device.isConnected;
107
+ });
108
+ resetDeviceButton.addEventListener("click", () => {
109
+ device.reset();
110
+ });
111
+
112
+ // DEVICE INFORMATION
113
+
114
+ /** @type {HTMLPreElement} */
115
+ const deviceInformationPre = document.getElementById("deviceInformationPre");
116
+ device.addEventListener("deviceInformation", () => {
117
+ deviceInformationPre.textContent = JSON.stringify(device.deviceInformation, null, 2);
118
+ });
119
+
120
+ // BATTERY LEVEL
121
+
122
+ /** @type {HTMLSpanElement} */
123
+ const batteryLevelSpan = document.getElementById("batteryLevel");
124
+ device.addEventListener("batteryLevel", () => {
125
+ console.log(`batteryLevel updated to ${device.batteryLevel}%`);
126
+ batteryLevelSpan.innerText = `${device.batteryLevel}%`;
127
+ });
128
+
129
+ /** @type {HTMLSpanElement} */
130
+ const isChargingSpan = document.getElementById("isCharging");
131
+ device.addEventListener("isCharging", () => {
132
+ console.log(`isCharging updated to ${device.isCharging}`);
133
+ isChargingSpan.innerText = device.isCharging;
134
+ });
135
+
136
+ /** @type {HTMLSpanElement} */
137
+ const batteryCurrentSpan = document.getElementById("batteryCurrent");
138
+ device.addEventListener("getBatteryCurrent", () => {
139
+ console.log(`batteryCurrent updated to ${device.batteryCurrent}mAh`);
140
+ batteryCurrentSpan.innerText = `${device.batteryCurrent}mAh`;
141
+ });
142
+
143
+ /** @type {HTMLButtonElement} */
144
+ const updateBatteryCurrentButton = document.getElementById("updateBatteryCurrent");
145
+ device.addEventListener("isConnected", () => {
146
+ updateBatteryCurrentButton.disabled = !device.isConnected;
147
+ });
148
+ updateBatteryCurrentButton.addEventListener("click", () => {
149
+ device.getBatteryCurrent();
150
+ });
151
+
152
+ // NAME
153
+
154
+ /** @type {HTMLSpanElement} */
155
+ const nameSpan = document.getElementById("name");
156
+ device.addEventListener("getName", () => {
157
+ console.log(`name updated to ${device.name}`);
158
+ nameSpan.innerText = device.name;
159
+ });
160
+
161
+ /** @type {HTMLInputElement} */
162
+ const setNameInput = document.getElementById("setNameInput");
163
+ setNameInput.minLength = BS.MinNameLength;
164
+ setNameInput.maxLength = BS.MaxNameLength;
165
+
166
+ /** @type {HTMLButtonElement} */
167
+ const setNameButton = document.getElementById("setNameButton");
168
+
169
+ device.addEventListener("isConnected", () => {
170
+ setNameInput.disabled = !device.isConnected;
171
+ });
172
+ device.addEventListener("notConnected", () => {
173
+ setNameInput.value = "";
174
+ });
175
+
176
+ setNameInput.addEventListener("input", () => {
177
+ setNameButton.disabled = setNameInput.value.length < device.minNameLength;
178
+ });
179
+
180
+ setNameButton.addEventListener("click", () => {
181
+ console.log(`setting name to ${setNameInput.value}`);
182
+ device.setName(setNameInput.value);
183
+ setNameInput.value = "";
184
+ setNameButton.disabled = true;
185
+ });
186
+
187
+ // TYPE
188
+
189
+ /** @type {HTMLSpanElement} */
190
+ const typeSpan = document.getElementById("type");
191
+ device.addEventListener("getType", () => {
192
+ console.log(`type updated to ${device.type}`);
193
+ typeSpan.innerText = device.type;
194
+ });
195
+
196
+ /** @type {HTMLButtonElement} */
197
+ const setTypeButton = document.getElementById("setTypeButton");
198
+
199
+ /** @type {HTMLSelectElement} */
200
+ const setTypeSelect = document.getElementById("setTypeSelect");
201
+ /** @type {HTMLOptGroupElement} */
202
+ const setTypeSelectOptgroup = setTypeSelect.querySelector("optgroup");
203
+ BS.DeviceTypes.forEach((type) => {
204
+ setTypeSelectOptgroup.appendChild(new Option(type));
205
+ });
206
+
207
+ device.addEventListener("isConnected", () => {
208
+ setTypeSelect.disabled = !device.isConnected;
209
+ });
210
+
211
+ device.addEventListener("getType", () => {
212
+ setTypeSelect.value = device.type;
213
+ });
214
+
215
+ setTypeSelect.addEventListener("input", () => {
216
+ setTypeButton.disabled = setTypeSelect.value == device.type;
217
+ });
218
+
219
+ setTypeButton.addEventListener("click", () => {
220
+ console.log(`setting type to ${setTypeSelect.value}`);
221
+ device.setType(setTypeSelect.value);
222
+ setTypeButton.disabled = true;
223
+ });
224
+
225
+ // SENSOR CONFIGURATION
226
+
227
+ /** @type {HTMLPreElement} */
228
+ const sensorConfigurationPre = document.getElementById("sensorConfigurationPre");
229
+ device.addEventListener("getSensorConfiguration", () => {
230
+ sensorConfigurationPre.textContent = JSON.stringify(device.sensorConfiguration, null, 2);
231
+ });
232
+
233
+ /** @type {HTMLTemplateElement} */
234
+ const sensorTypeConfigurationTemplate = document.getElementById("sensorTypeConfigurationTemplate");
235
+ BS.SensorTypes.forEach((sensorType) => {
236
+ /** @type {HTMLElement} */
237
+ const sensorTypeConfigurationContainer = sensorTypeConfigurationTemplate.content
238
+ .cloneNode(true)
239
+ .querySelector(".sensorTypeConfiguration");
240
+ sensorTypeConfigurationContainer.querySelector(".sensorType").innerText = sensorType;
241
+
242
+ /** @type {HTMLInputElement} */
243
+ const sensorRateInput = sensorTypeConfigurationContainer.querySelector(".sensorRate");
244
+ sensorRateInput.value = 0;
245
+ sensorRateInput.max = BS.MaxSensorRate;
246
+ sensorRateInput.step = BS.SensorRateStep;
247
+ sensorRateInput.addEventListener("input", () => {
248
+ const sensorRate = Number(sensorRateInput.value);
249
+ console.log({ sensorType, sensorRate });
250
+ device.setSensorConfiguration({ [sensorType]: sensorRate });
251
+ });
252
+
253
+ device.addEventListener("connected", () => {
254
+ if (device.sensorTypes.includes(sensorType)) {
255
+ sensorTypeConfigurationContainer.classList.remove("hidden");
256
+ } else {
257
+ sensorTypeConfigurationContainer.classList.add("hidden");
258
+ }
259
+ });
260
+
261
+ sensorTypeConfigurationTemplate.parentElement.appendChild(sensorTypeConfigurationContainer);
262
+ sensorTypeConfigurationContainer.dataset.sensorType = sensorType;
263
+ });
264
+ device.addEventListener("getSensorConfiguration", () => {
265
+ for (const sensorType in device.sensorConfiguration) {
266
+ document.querySelector(`.sensorTypeConfiguration[data-sensor-type="${sensorType}"] input`).value =
267
+ device.sensorConfiguration[sensorType];
268
+ }
269
+ });
270
+ device.addEventListener("isConnected", () => {
271
+ for (const sensorType in device.sensorConfiguration) {
272
+ document.querySelector(`[data-sensor-type="${sensorType}"] input`).disabled = !device.isConnected;
273
+ }
274
+ });
275
+
276
+ // SENSOR DATA
277
+
278
+ /** @type {HTMLTemplateElement} */
279
+ const sensorTypeDataTemplate = document.getElementById("sensorTypeDataTemplate");
280
+ BS.SensorTypes.forEach((sensorType) => {
281
+ const sensorTypeDataContainer = sensorTypeDataTemplate.content.cloneNode(true).querySelector(".sensorTypeData");
282
+ sensorTypeDataContainer.querySelector(".sensorType").innerText = sensorType;
283
+
284
+ /** @type {HTMLPreElement} */
285
+ const sensorDataPre = sensorTypeDataContainer.querySelector(".sensorData");
286
+ device.addEventListener(sensorType, (event) => {
287
+ const sensorData = event.message;
288
+ sensorDataPre.textContent = JSON.stringify(sensorData, null, 2);
289
+ });
290
+
291
+ sensorTypeDataTemplate.parentElement.appendChild(sensorTypeDataContainer);
292
+ sensorTypeDataContainer.dataset.sensorType = sensorType;
293
+ });
294
+
295
+ // VIBRATION
296
+ /** @type {HTMLTemplateElement} */
297
+ const vibrationTemplate = document.getElementById("vibrationTemplate");
298
+ {
299
+ /** @type {HTMLInputElement} */
300
+ const waveformEffectSequenceLoopCountInput = vibrationTemplate.content.querySelector(
301
+ ".waveformEffect .sequenceLoopCount"
302
+ );
303
+ waveformEffectSequenceLoopCountInput.max = BS.MaxVibrationWaveformEffectSequenceLoopCount;
304
+ }
305
+ /** @type {HTMLTemplateElement} */
306
+ const vibrationLocationTemplate = document.getElementById("vibrationLocationTemplate");
307
+
308
+ /** @type {HTMLTemplateElement} */
309
+ const waveformEffectSegmentTemplate = document.getElementById("waveformEffectSegmentTemplate");
310
+ {
311
+ /** @type {HTMLSelectElement} */
312
+ const waveformEffectSelect = waveformEffectSegmentTemplate.content.querySelector(".effect");
313
+ const waveformEffectOptgroup = waveformEffectSelect.querySelector("optgroup");
314
+ BS.VibrationWaveformEffects.forEach((waveformEffect) => {
315
+ waveformEffectOptgroup.appendChild(new Option(waveformEffect));
316
+ });
317
+
318
+ /** @type {HTMLInputElement} */
319
+ const waveformEffectSegmentDelayInput = waveformEffectSegmentTemplate.content.querySelector(".delay");
320
+ waveformEffectSegmentDelayInput.max = BS.MaxVibrationWaveformEffectSegmentDelay;
321
+
322
+ /** @type {HTMLInputElement} */
323
+ const waveformEffectLoopCountInput = waveformEffectSegmentTemplate.content.querySelector(".loopCount");
324
+ waveformEffectLoopCountInput.max = BS.MaxVibrationWaveformEffectSegmentLoopCount;
325
+ }
326
+
327
+ /** @type {HTMLTemplateElement} */
328
+ const waveformSegmentTemplate = document.getElementById("waveformSegmentTemplate");
329
+ {
330
+ /** @type {HTMLInputElement} */
331
+ const waveformDurationSegmentInput = waveformSegmentTemplate.content.querySelector(".duration");
332
+ waveformDurationSegmentInput.max = BS.MaxVibrationWaveformSegmentDuration;
333
+ }
334
+
335
+ /** @type {HTMLButtonElement} */
336
+ const addVibrationButton = document.getElementById("addVibration");
337
+ addVibrationButton.addEventListener("click", () => {
338
+ /** @type {HTMLElement} */
339
+ const vibrationContainer = vibrationTemplate.content.cloneNode(true).querySelector(".vibration");
340
+
341
+ /** @type {HTMLButtonElement} */
342
+ const deleteButton = vibrationContainer.querySelector(".delete");
343
+ deleteButton.addEventListener("click", () => {
344
+ vibrationContainer.remove();
345
+ updateTriggerVibrationsButtonDisabled();
346
+ });
347
+
348
+ /** @type {HTMLUListElement} */
349
+ const vibrationLocationsContainer = vibrationContainer.querySelector(".locations");
350
+ BS.VibrationLocations.forEach((vibrationLocation) => {
351
+ const vibrationLocationContainer = vibrationLocationTemplate.content
352
+ .cloneNode(true)
353
+ .querySelector(".vibrationLocation");
354
+ vibrationLocationContainer.querySelector("span").innerText = vibrationLocation;
355
+ vibrationLocationContainer.querySelector("input").dataset.vibrationLocation = vibrationLocation;
356
+ vibrationLocationsContainer.appendChild(vibrationLocationContainer);
357
+ });
358
+
359
+ /** @type {HTMLElement} */
360
+ const waveformEffectContainer = vibrationContainer.querySelector(".waveformEffect");
361
+ /** @type {HTMLUListElement} */
362
+ const waveformEffectSegmentsContainer = waveformEffectContainer.querySelector(".segments");
363
+ /** @type {HTMLButtonElement} */
364
+ const addWaveformEffectSegmentButton = waveformEffectContainer.querySelector(".add");
365
+ const updateAddWaveformEffectSegmentButton = () => {
366
+ addWaveformEffectSegmentButton.disabled =
367
+ waveformEffectSegmentsContainer.children.length >= BS.MaxNumberOfVibrationWaveformEffectSegments;
368
+ };
369
+ addWaveformEffectSegmentButton.addEventListener("click", () => {
370
+ /** @type {HTMLElement} */
371
+ const waveformEffectSegmentContainer = waveformEffectSegmentTemplate.content
372
+ .cloneNode(true)
373
+ .querySelector(".waveformEffectSegment");
374
+
375
+ const effectContainer = waveformEffectSegmentContainer.querySelector(".effect").parentElement;
376
+ const delayContainer = waveformEffectSegmentContainer.querySelector(".delay").parentElement;
377
+
378
+ /** @type {HTMLSelectElement} */
379
+ const waveformEffectTypeSelect = waveformEffectSegmentContainer.querySelector(".type");
380
+ waveformEffectTypeSelect.addEventListener("input", () => {
381
+ let shouldShowEffectContainer = false;
382
+ let shouldShowDelayContainer = false;
383
+
384
+ switch (waveformEffectTypeSelect.value) {
385
+ case "effect":
386
+ shouldShowEffectContainer = true;
387
+ break;
388
+ case "delay":
389
+ shouldShowDelayContainer = true;
390
+ break;
391
+ default:
392
+ throw Error(`uncaught waveformEffectTypeSelect value "${waveformEffectTypeSelect.value}"`);
393
+ }
394
+
395
+ effectContainer.style.display = shouldShowEffectContainer ? "" : "none";
396
+ delayContainer.style.display = shouldShowDelayContainer ? "" : "none";
397
+ });
398
+ waveformEffectTypeSelect.dispatchEvent(new Event("input"));
399
+
400
+ waveformEffectSegmentContainer.querySelector(".delete").addEventListener("click", () => {
401
+ waveformEffectSegmentContainer.remove();
402
+ updateAddWaveformEffectSegmentButton();
403
+ });
404
+
405
+ waveformEffectSegmentsContainer.appendChild(waveformEffectSegmentContainer);
406
+ updateAddWaveformEffectSegmentButton();
407
+ });
408
+
409
+ /** @type {HTMLElement} */
410
+ const waveformContainer = vibrationContainer.querySelector(".waveform");
411
+ /** @type {HTMLUListElement} */
412
+ const waveformSegmentsContainer = waveformContainer.querySelector(".segments");
413
+
414
+ /** @type {HTMLButtonElement} */
415
+ const addWaveformSegmentButton = waveformContainer.querySelector(".add");
416
+ const updateAddWaveformSegmentButton = () => {
417
+ addWaveformSegmentButton.disabled =
418
+ waveformSegmentsContainer.children.length >= BS.MaxNumberOfVibrationWaveformSegments;
419
+ };
420
+ addWaveformSegmentButton.addEventListener("click", () => {
421
+ /** @type {HTMLElement} */
422
+ const waveformSegmentContainer = waveformSegmentTemplate.content.cloneNode(true).querySelector(".waveformSegment");
423
+
424
+ waveformSegmentContainer.querySelector(".delete").addEventListener("click", () => {
425
+ waveformSegmentContainer.remove();
426
+ updateAddWaveformSegmentButton();
427
+ });
428
+
429
+ waveformSegmentsContainer.appendChild(waveformSegmentContainer);
430
+ updateAddWaveformSegmentButton();
431
+ });
432
+
433
+ /** @type {HTMLSelectElement} */
434
+ const vibrationTypeSelect = vibrationContainer.querySelector(".type");
435
+ /** @type {HTMLOptGroupElement} */
436
+ const vibrationTypeSelectOptgroup = vibrationTypeSelect.querySelector("optgroup");
437
+ BS.VibrationTypes.forEach((vibrationType) => {
438
+ vibrationTypeSelectOptgroup.appendChild(new Option(vibrationType));
439
+ });
440
+
441
+ vibrationTypeSelect.addEventListener("input", () => {
442
+ let showWaveformContainer = false;
443
+ let showWaveformEffectContainer = false;
444
+
445
+ /** @type {BS.VibrationType} */
446
+ const vibrationType = vibrationTypeSelect.value;
447
+ switch (vibrationType) {
448
+ case "waveform":
449
+ showWaveformContainer = true;
450
+ break;
451
+ case "waveformEffect":
452
+ showWaveformEffectContainer = true;
453
+ break;
454
+ default:
455
+ throw Error(`invalid vibrationType "${vibrationType}"`);
456
+ }
457
+
458
+ waveformEffectContainer.style.display = showWaveformEffectContainer ? "" : "none";
459
+ waveformContainer.style.display = showWaveformContainer ? "" : "none";
460
+ });
461
+ vibrationTypeSelect.dispatchEvent(new Event("input"));
462
+
463
+ vibrationTemplate.parentElement.appendChild(vibrationContainer);
464
+
465
+ updateTriggerVibrationsButtonDisabled();
466
+ });
467
+
468
+ const triggerVibrationsButton = document.getElementById("triggerVibrations");
469
+ triggerVibrationsButton.addEventListener("click", () => {
470
+ /** @type {BS.VibrationConfiguration[]} */
471
+ let vibrationConfigurations = [];
472
+ Array.from(vibrationTemplate.parentElement.querySelectorAll(".vibration"))
473
+ .filter((vibrationContainer) => vibrationContainer.querySelector(".shouldTrigger").checked)
474
+ .forEach((vibrationContainer) => {
475
+ /** @type {BS.VibrationConfiguration} */
476
+ const vibrationConfiguration = {
477
+ locations: [],
478
+ };
479
+ Array.from(vibrationContainer.querySelectorAll(`[data-vibration-location]`))
480
+ .filter((input) => input.checked)
481
+ .forEach((input) => {
482
+ vibrationConfiguration.locations.push(input.dataset.vibrationLocation);
483
+ });
484
+ if (vibrationConfiguration.locations.length == 0) {
485
+ return;
486
+ }
487
+
488
+ vibrationConfiguration.type = vibrationContainer.querySelector("select.type").value;
489
+ switch (vibrationConfiguration.type) {
490
+ case "waveformEffect":
491
+ vibrationConfiguration.segments = Array.from(
492
+ vibrationContainer.querySelectorAll(".waveformEffect .waveformEffectSegment")
493
+ ).map((waveformEffectSegmentContainer) => {
494
+ /** @type {BS.VibrationWaveformEffectSegment} */
495
+ const waveformEffectSegment = {
496
+ loopCount: Number(waveformEffectSegmentContainer.querySelector(".loopCount").value),
497
+ };
498
+ if (waveformEffectSegmentContainer.querySelector(".type").value == "effect") {
499
+ waveformEffectSegment.effect = waveformEffectSegmentContainer.querySelector(".effect").value;
500
+ } else {
501
+ waveformEffectSegment.delay = Number(waveformEffectSegmentContainer.querySelector(".delay").value);
502
+ }
503
+ return waveformEffectSegment;
504
+ });
505
+ vibrationConfiguration.loopCount = Number(
506
+ vibrationContainer.querySelector(".waveformEffect .sequenceLoopCount").value
507
+ );
508
+ break;
509
+ case "waveform":
510
+ vibrationConfiguration.segments = Array.from(
511
+ vibrationContainer.querySelectorAll(".waveform .waveformSegment")
512
+ ).map((waveformSegmentContainer) => {
513
+ return {
514
+ amplitude: Number(waveformSegmentContainer.querySelector(".amplitude").value),
515
+ duration: Number(waveformSegmentContainer.querySelector(".duration").value),
516
+ };
517
+ });
518
+ break;
519
+ default:
520
+ throw Error(`invalid vibrationType "${vibrationConfiguration.type}"`);
521
+ }
522
+ vibrationConfigurations.push(vibrationConfiguration);
523
+ });
524
+ console.log({ vibrationConfigurations });
525
+ if (vibrationConfigurations.length > 0) {
526
+ device.triggerVibration(vibrationConfigurations);
527
+ }
528
+ });
529
+ device.addEventListener("isConnected", () => {
530
+ updateTriggerVibrationsButtonDisabled();
531
+ });
532
+
533
+ function updateTriggerVibrationsButtonDisabled() {
534
+ triggerVibrationsButton.disabled =
535
+ !device.isConnected || vibrationTemplate.parentElement.querySelectorAll(".vibration").length == 0;
536
+ }
537
+
538
+ // FILE TRANSFER
539
+
540
+ /** @type {File?} */
541
+ let file;
542
+
543
+ /** @type {HTMLInputElement} */
544
+ const fileInput = document.getElementById("file");
545
+ fileInput.addEventListener("input", () => {
546
+ if (fileInput.files[0].size > device.maxFileLength) {
547
+ console.log("file size too large");
548
+ return;
549
+ }
550
+ file = fileInput.files[0];
551
+ console.log("file", file);
552
+ updateToggleFileTransferButton();
553
+ });
554
+
555
+ const maxFileLengthSpan = document.getElementById("maxFileLength");
556
+ const updateMaxFileLengthSpan = () => {
557
+ maxFileLengthSpan.innerText = (device.maxFileLength / 1024).toLocaleString();
558
+ };
559
+ updateMaxFileLengthSpan();
560
+ device.addEventListener("isConnected", () => {
561
+ updateMaxFileLengthSpan();
562
+ });
563
+
564
+ /** @type {BS.FileType} */
565
+ let fileType;
566
+
567
+ /** @type {HTMLSelectElement} */
568
+ const fileTransferTypesSelect = document.getElementById("fileTransferTypes");
569
+ fileTransferTypesSelect.addEventListener("input", () => {
570
+ fileType = fileTransferTypesSelect.value;
571
+ console.log({ fileType });
572
+ switch (fileType) {
573
+ case "tflite":
574
+ fileInput.accept = ".tflite";
575
+ break;
576
+ }
577
+ });
578
+ /** @type {HTMLOptGroupElement} */
579
+ const fileTransferTypesOptgroup = fileTransferTypesSelect.querySelector("optgroup");
580
+ BS.FileTypes.forEach((fileType) => {
581
+ fileTransferTypesOptgroup.appendChild(new Option(fileType));
582
+ });
583
+ fileTransferTypesSelect.dispatchEvent(new Event("input"));
584
+
585
+ /** @type {HTMLProgressElement} */
586
+ const fileTransferProgress = document.getElementById("fileTransferProgress");
587
+
588
+ device.addEventListener("fileTransferProgress", (event) => {
589
+ const progress = event.message.progress;
590
+ console.log({ progress });
591
+ fileTransferProgress.value = progress == 1 ? 0 : progress;
592
+ });
593
+ device.addEventListener("fileTransferStatus", () => {
594
+ if (device.fileTransferStatus == "idle") {
595
+ fileTransferProgress.value = 0;
596
+ }
597
+ });
598
+
599
+ /** @type {HTMLButtonElement} */
600
+ const toggleFileTransferButton = document.getElementById("toggleFileTransfer");
601
+ toggleFileTransferButton.addEventListener("click", async () => {
602
+ if (device.fileTransferStatus == "idle") {
603
+ if (fileTransferDirection == "send") {
604
+ if (fileType == "tflite") {
605
+ await device.setTfliteName(file.name.replaceAll(".tflite", ""));
606
+ }
607
+ device.sendFile(fileType, file);
608
+ } else {
609
+ device.receiveFile(fileType);
610
+ }
611
+ } else {
612
+ device.cancelFileTransfer();
613
+ }
614
+ });
615
+ const updateToggleFileTransferButton = () => {
616
+ const enabled = device.isConnected && (file || fileTransferDirection == "receive");
617
+ toggleFileTransferButton.disabled = !enabled;
618
+
619
+ /** @type {String} */
620
+ let innerText;
621
+ switch (device.fileTransferStatus) {
622
+ case "idle":
623
+ innerText = `${fileTransferDirection} file`;
624
+ break;
625
+ case "sending":
626
+ innerText = "stop sending file";
627
+ break;
628
+ case "receiving":
629
+ innerText = "stop receiving file";
630
+ break;
631
+ }
632
+ toggleFileTransferButton.innerText = innerText;
633
+ };
634
+ device.addEventListener("isConnected", () => {
635
+ updateToggleFileTransferButton();
636
+ });
637
+ device.addEventListener("fileTransferStatus", () => {
638
+ updateToggleFileTransferButton();
639
+ });
640
+
641
+ /** @type {BS.FileTransferDirection} */
642
+ let fileTransferDirection;
643
+ /** @type {HTMLSelectElement} */
644
+ const fileTransferDirectionSelect = document.getElementById("fileTransferDirection");
645
+ fileTransferDirectionSelect.addEventListener("input", () => {
646
+ fileTransferDirection = fileTransferDirectionSelect.value;
647
+ console.log({ fileTransferDirection });
648
+ updateToggleFileTransferButton();
649
+ });
650
+ fileTransferDirectionSelect.dispatchEvent(new Event("input"));
651
+
652
+ /** @param {File} file */
653
+ function downloadFile(file) {
654
+ const a = document.createElement("a");
655
+ document.body.appendChild(a);
656
+ a.style = "display: none";
657
+ const url = window.URL.createObjectURL(file);
658
+ a.href = url;
659
+ a.download = file.name;
660
+ a.click();
661
+ window.URL.revokeObjectURL(url);
662
+ }
663
+
664
+ device.addEventListener("fileReceived", (event) => {
665
+ const file = event.message.file;
666
+ downloadFile(file);
667
+ });
668
+
669
+ // TFLITE
670
+
671
+ /** @type {HTMLSpanElement} */
672
+ const tfliteNameSpan = document.getElementById("tfliteName");
673
+ /** @type {HTMLInputElement} */
674
+ const setTfliteNameInput = document.getElementById("setTfliteNameInput");
675
+ /** @type {HTMLButtonElement} */
676
+ const setTfliteNameButton = document.getElementById("setTfliteNameButton");
677
+
678
+ function updateSetTfliteNameButton() {
679
+ const enabled = device.isConnected && setTfliteNameInput.value.length > 0;
680
+ setTfliteNameButton.disabled = !enabled;
681
+ }
682
+
683
+ device.addEventListener("isConnected", () => {
684
+ const disabled = !device.isConnected;
685
+ setTfliteNameInput.disabled = disabled;
686
+ updateSetTfliteNameButton();
687
+ });
688
+
689
+ setTfliteNameInput.addEventListener("input", () => {
690
+ updateSetTfliteNameButton();
691
+ });
692
+
693
+ device.addEventListener("getTfliteName", () => {
694
+ tfliteNameSpan.innerText = device.tfliteName;
695
+
696
+ setTfliteNameButton.innerText = "set name";
697
+ setTfliteNameButton.disabled = !device.isConnected;
698
+
699
+ setTfliteNameInput.value = "";
700
+ setTfliteNameInput.disabled = false;
701
+ updateSetTfliteNameButton();
702
+ });
703
+
704
+ setTfliteNameButton.addEventListener("click", () => {
705
+ device.setTfliteName(setTfliteNameInput.value);
706
+
707
+ setTfliteNameInput.disabled = true;
708
+
709
+ setTfliteNameButton.innerText = "setting name...";
710
+ setTfliteNameButton.disabled = true;
711
+ });
712
+
713
+ /** @type {HTMLSpanElement} */
714
+ const tfliteTaskSpan = document.getElementById("tfliteTask");
715
+ /** @type {HTMLSelectElement} */
716
+ const setTfliteTaskSelect = document.getElementById("setTfliteTaskSelect");
717
+ /** @type {HTMLOptGroupElement} */
718
+ const setTfliteTaskOptgroup = setTfliteTaskSelect.querySelector("optgroup");
719
+ /** @type {HTMLButtonElement} */
720
+ const setTfliteTaskButton = document.getElementById("setTfliteTaskButton");
721
+
722
+ device.addEventListener("isConnected", () => {
723
+ const disabled = !device.isConnected;
724
+ setTfliteTaskSelect.disabled = disabled;
725
+ setTfliteTaskButton.disabled = disabled;
726
+ });
727
+
728
+ BS.TfliteTasks.forEach((task) => {
729
+ setTfliteTaskOptgroup.appendChild(new Option(task));
730
+ });
731
+
732
+ device.addEventListener("getTfliteTask", () => {
733
+ const task = device.tfliteTask;
734
+ setTfliteTaskSelect.value = task;
735
+ tfliteTaskSpan.innerText = task;
736
+ });
737
+
738
+ setTfliteTaskButton.addEventListener("click", () => {
739
+ device.setTfliteTask(setTfliteTaskSelect.value);
740
+ });
741
+ device.addEventListener("getTfliteInferencingEnabled", () => {
742
+ setTfliteTaskButton.disabled = device.tfliteInferencingEnabled;
743
+ });
744
+
745
+ /** @type {HTMLSpanElement} */
746
+ const tfliteSampleRateSpan = document.getElementById("tfliteSampleRate");
747
+ /** @type {HTMLInputElement} */
748
+ const setTfliteSampleRateInput = document.getElementById("setTfliteSampleRateInput");
749
+ /** @type {HTMLButtonElement} */
750
+ const setTfliteSampleRateButton = document.getElementById("setTfliteSampleRateButton");
751
+
752
+ device.addEventListener("isConnected", () => {
753
+ const disabled = !device.isConnected;
754
+ setTfliteSampleRateInput.disabled = disabled;
755
+ setTfliteSampleRateButton.disabled = disabled;
756
+ });
757
+
758
+ device.addEventListener("getTfliteSampleRate", () => {
759
+ tfliteSampleRateSpan.innerText = device.tfliteSampleRate;
760
+
761
+ setTfliteSampleRateInput.value = "";
762
+ setTfliteSampleRateInput.disabled = false;
763
+
764
+ setTfliteSampleRateButton.disabled = false;
765
+ setTfliteSampleRateButton.innerText = "set sample rate";
766
+ });
767
+
768
+ setTfliteSampleRateButton.addEventListener("click", () => {
769
+ device.setTfliteSampleRate(Number(setTfliteSampleRateInput.value));
770
+
771
+ setTfliteSampleRateInput.disabled = true;
772
+
773
+ setTfliteSampleRateButton.disabled = true;
774
+ setTfliteSampleRateButton.innerText = "setting sample rate...";
775
+ });
776
+ device.addEventListener("getTfliteInferencingEnabled", () => {
777
+ setTfliteSampleRateButton.disabled = device.tfliteInferencingEnabled;
778
+ });
779
+
780
+ const tfliteSensorTypesContainer = document.getElementById("tfliteSensorTypes");
781
+ /** @type {HTMLTemplateElement} */
782
+ const tfliteSensorTypeTemplate = document.getElementById("tfliteSensorTypeTemplate");
783
+ /** @type {Object.<string, HTMLElement>} */
784
+ const tfliteSensorTypeContainers = {};
785
+ /** @type {BS.SensorType[]} */
786
+ let tfliteSensorTypes = [];
787
+ /** @type {HTMLButtonElement} */
788
+ const setTfliteSensorTypesButton = document.getElementById("setTfliteSensorTypes");
789
+
790
+ BS.TfliteSensorTypes.forEach((sensorType) => {
791
+ const sensorTypeContainer = tfliteSensorTypeTemplate.content.cloneNode(true).querySelector(".sensorType");
792
+ sensorTypeContainer.querySelector(".name").innerText = sensorType;
793
+
794
+ /** @type {HTMLInputElement} */
795
+ const isSensorEnabledInput = sensorTypeContainer.querySelector(".enabled");
796
+ isSensorEnabledInput.addEventListener("input", () => {
797
+ if (isSensorEnabledInput.checked) {
798
+ tfliteSensorTypes.push(sensorType);
799
+ } else {
800
+ tfliteSensorTypes.splice(tfliteSensorTypes.indexOf(sensorType), 1);
801
+ }
802
+ console.log("tfliteSensorTypes", tfliteSensorTypes);
803
+ });
804
+
805
+ device.addEventListener("getTfliteSensorTypes", () => {
806
+ isSensorEnabledInput.checked = device.tfliteSensorTypes.includes(sensorType);
807
+ });
808
+ isSensorEnabledInput.checked = device.tfliteSensorTypes.includes(sensorType);
809
+
810
+ tfliteSensorTypeContainers[sensorType] = sensorTypeContainer;
811
+
812
+ tfliteSensorTypesContainer.appendChild(sensorTypeContainer);
813
+ });
814
+
815
+ device.addEventListener("getTfliteSensorTypes", () => {
816
+ tfliteSensorTypes = device.tfliteSensorTypes;
817
+ });
818
+
819
+ setTfliteSensorTypesButton.addEventListener("click", () => {
820
+ setTfliteSensorTypesButton.disabled = true;
821
+ setTfliteSensorTypesButton.innerText = "setting sensor types...";
822
+ device.setTfliteSensorTypes(tfliteSensorTypes);
823
+ });
824
+ device.addEventListener("getTfliteSensorTypes", () => {
825
+ setTfliteSensorTypesButton.disabled = false;
826
+ setTfliteSensorTypesButton.innerText = "set sensor types";
827
+ });
828
+ device.addEventListener("getTfliteInferencingEnabled", () => {
829
+ setTfliteSensorTypesButton.disabled = device.tfliteInferencingEnabled;
830
+ });
831
+
832
+ /** @type {HTMLInputElement} */
833
+ const setTfliteIsReadyInput = document.getElementById("tfliteIsReady");
834
+ device.addEventListener("tfliteIsReady", () => {
835
+ setTfliteIsReadyInput.checked = device.tfliteIsReady;
836
+ });
837
+
838
+ /** @type {HTMLSpanElement} */
839
+ const tfliteThresholdSpan = document.getElementById("tfliteThreshold");
840
+ /** @type {HTMLInputElement} */
841
+ const setTfliteThresholdInput = document.getElementById("setTfliteThresholdInput");
842
+ /** @type {HTMLButtonElement} */
843
+ const setTfliteThresholdButton = document.getElementById("setTfliteThresholdButton");
844
+
845
+ device.addEventListener("isConnected", () => {
846
+ const disabled = !device.isConnected;
847
+ setTfliteThresholdInput.disabled = disabled;
848
+ setTfliteThresholdButton.disabled = disabled;
849
+ });
850
+
851
+ device.addEventListener("getTfliteThreshold", () => {
852
+ tfliteThresholdSpan.innerText = device.tfliteThreshold;
853
+
854
+ setTfliteThresholdInput.value = "";
855
+ setTfliteThresholdInput.disabled = false;
856
+
857
+ setTfliteThresholdButton.disabled = false;
858
+ setTfliteThresholdButton.innerText = "set threshold";
859
+ });
860
+
861
+ setTfliteThresholdButton.addEventListener("click", () => {
862
+ device.setTfliteThreshold(Number(setTfliteThresholdInput.value));
863
+
864
+ setTfliteThresholdInput.disabled = true;
865
+
866
+ setTfliteThresholdButton.disabled = true;
867
+ setTfliteThresholdButton.innerText = "setting threshold...";
868
+ });
869
+
870
+ /** @type {HTMLSpanElement} */
871
+ const tfliteCaptureDelaySpan = document.getElementById("tfliteCaptureDelay");
872
+ /** @type {HTMLInputElement} */
873
+ const setTfliteCaptureDelayInput = document.getElementById("setTfliteCaptureDelayInput");
874
+ /** @type {HTMLButtonElement} */
875
+ const setTfliteCaptureDelayButton = document.getElementById("setTfliteCaptureDelayButton");
876
+
877
+ device.addEventListener("isConnected", () => {
878
+ const disabled = !device.isConnected;
879
+ setTfliteCaptureDelayInput.disabled = disabled;
880
+ setTfliteCaptureDelayButton.disabled = disabled;
881
+ });
882
+
883
+ device.addEventListener("getTfliteCaptureDelay", () => {
884
+ tfliteCaptureDelaySpan.innerText = device.tfliteCaptureDelay;
885
+
886
+ setTfliteCaptureDelayInput.value = "";
887
+ setTfliteCaptureDelayInput.disabled = false;
888
+
889
+ setTfliteCaptureDelayButton.disabled = false;
890
+ setTfliteCaptureDelayButton.innerText = "set capture delay";
891
+ });
892
+
893
+ setTfliteCaptureDelayButton.addEventListener("click", () => {
894
+ device.setTfliteCaptureDelay(Number(setTfliteCaptureDelayInput.value));
895
+
896
+ setTfliteCaptureDelayInput.disabled = true;
897
+
898
+ setTfliteCaptureDelayButton.disabled = true;
899
+ setTfliteCaptureDelayButton.innerText = "setting capture delay...";
900
+ });
901
+
902
+ /** @type {HTMLInputElement} */
903
+ const tfliteInferencingEnabledInput = document.getElementById("tfliteInferencingEnabled");
904
+ /** @type {HTMLButtonElement} */
905
+ const toggleTfliteInferencingEnabledButton = document.getElementById("toggleTfliteInferencingEnabled");
906
+
907
+ device.addEventListener("tfliteIsReady", () => {
908
+ toggleTfliteInferencingEnabledButton.disabled = !device.tfliteIsReady;
909
+ });
910
+ device.addEventListener("getTfliteInferencingEnabled", () => {
911
+ tfliteInferencingEnabledInput.checked = device.tfliteInferencingEnabled;
912
+ toggleTfliteInferencingEnabledButton.innerText = device.tfliteInferencingEnabled
913
+ ? "disable inferencing"
914
+ : "enable inferencing";
915
+ });
916
+
917
+ toggleTfliteInferencingEnabledButton.addEventListener("click", () => {
918
+ device.toggleTfliteInferencing();
919
+ });
920
+
921
+ /** @type {String[]} */
922
+ let inferenceClasses = [];
923
+
924
+ /** @type {HTMLTextAreaElement} */
925
+ const inferenceClassesTextArea = document.getElementById("inferenceClasses");
926
+ inferenceClassesTextArea.addEventListener("input", () => {
927
+ inferenceClasses = inferenceClassesTextArea.value.split("\n").filter(Boolean);
928
+ console.log("inferenceClasses", inferenceClasses);
929
+ localStorage.setItem("BS.inferenceClasses", JSON.stringify(inferenceClasses));
930
+ });
931
+ if (localStorage.getItem("BS.inferenceClasses")) {
932
+ inferenceClasses = JSON.parse(localStorage.getItem("BS.inferenceClasses"));
933
+ inferenceClassesTextArea.value = inferenceClasses.join("\n");
934
+ }
935
+
936
+ /** @type {HTMLElement} */
937
+ const topInferenceClassElement = document.getElementById("topInferenceClass");
938
+
939
+ /** @type {HTMLPreElement} */
940
+ const tfliteInferencePre = document.getElementById("tfliteInference");
941
+ device.addEventListener("tfliteInference", (event) => {
942
+ const { tfliteInference } = event.message;
943
+ console.log("inference", tfliteInference);
944
+ tfliteInferencePre.textContent = JSON.stringify(tfliteInference, null, 2);
945
+
946
+ if (device.tfliteTask == "classification") {
947
+ topInferenceClassElement.innerText = inferenceClasses[tfliteInference.maxIndex - 1] ?? "";
948
+ }
949
+ });
950
+
951
+ // FIRMWARE
952
+
953
+ /** @type {File?} */
954
+ let firmware;
955
+
956
+ /** @type {HTMLInputElement} */
957
+ const firmwareInput = document.getElementById("firmwareInput");
958
+ firmwareInput.addEventListener("input", () => {
959
+ firmware = firmwareInput.files[0];
960
+ updateToggleFirmwareUploadButton();
961
+ });
962
+ /** @type {HTMLButtonElement} */
963
+ const toggleFirmwareUploadButton = document.getElementById("toggleFirmwareUpload");
964
+ toggleFirmwareUploadButton.addEventListener("click", () => {
965
+ device.uploadFirmware(firmware);
966
+ });
967
+ const updateToggleFirmwareUploadButton = () => {
968
+ const enabled = device.isConnected && Boolean(firmware);
969
+ toggleFirmwareUploadButton.disabled = !enabled;
970
+ };
971
+ device.addEventListener("isConnected", () => {
972
+ updateToggleFirmwareUploadButton();
973
+ });
974
+
975
+ /** @type {HTMLProgressElement} */
976
+ const firmwareUploadProgress = document.getElementById("firmwareUploadProgress");
977
+ /** @type {HTMLSpanElement} */
978
+ const firmwareUploadProgressPercentageSpan = document.getElementById("firmwareUploadProgressPercentage");
979
+ device.addEventListener("firmwareUploadProgress", (event) => {
980
+ const progress = event.message.progress;
981
+ firmwareUploadProgress.value = progress;
982
+ firmwareUploadProgressPercentageSpan.innerText = `${Math.floor(100 * progress)}%`;
983
+ });
984
+ device.addEventListener("firmwareUploadComplete", () => {
985
+ firmwareUploadProgress.value = 0;
986
+ });
987
+ device.addEventListener("firmwareStatus", () => {
988
+ const isUploading = device.firmwareStatus == "uploading";
989
+ firmwareUploadProgressPercentageSpan.style.display = isUploading ? "" : "none";
990
+ });
991
+
992
+ /** @type {HTMLPreElement} */
993
+ const firmwareImagesPre = document.getElementById("firmwareImages");
994
+ device.addEventListener("firmwareImages", () => {
995
+ firmwareImagesPre.textContent = JSON.stringify(
996
+ device.firmwareImages,
997
+ (key, value) => (key == "hash" ? Array.from(value).join(",") : value),
998
+ 2
999
+ );
1000
+ });
1001
+
1002
+ device.addEventListener("isConnected", () => {
1003
+ if (device.isConnected) {
1004
+ device.getFirmwareImages();
1005
+ }
1006
+ });
1007
+
1008
+ /** @type {HTMLSpanElement} */
1009
+ const firmwareStatusSpan = document.getElementById("firmwareStatus");
1010
+ device.addEventListener("firmwareStatus", () => {
1011
+ firmwareStatusSpan.innerText = device.firmwareStatus;
1012
+
1013
+ updateResetButton();
1014
+ updateTestFirmwareImageButton();
1015
+ updateConfirmFirmwareImageButton();
1016
+ updateEraseFirmwareImageButton();
1017
+ updateSelectImageSelect();
1018
+ });
1019
+
1020
+ /** @type {HTMLButtonElement} */
1021
+ const resetButton = document.getElementById("reset");
1022
+ resetButton.addEventListener("click", () => {
1023
+ device.reset();
1024
+ resetButton.disabled = true;
1025
+ });
1026
+ const updateResetButton = () => {
1027
+ const status = device.firmwareStatus;
1028
+ const enabled = status == "pending" || status == "testing";
1029
+ resetButton.disabled = !enabled;
1030
+ };
1031
+
1032
+ /** @type {HTMLButtonElement} */
1033
+ const testFirmwareImageButton = document.getElementById("testFirmwareImage");
1034
+ testFirmwareImageButton.addEventListener("click", () => {
1035
+ device.testFirmwareImage(selectedImageIndex);
1036
+ });
1037
+ const updateTestFirmwareImageButton = () => {
1038
+ const enabled = device.firmwareStatus == "uploaded";
1039
+ testFirmwareImageButton.disabled = !enabled;
1040
+ };
1041
+
1042
+ /** @type {HTMLButtonElement} */
1043
+ const confirmFirmwareImageButton = document.getElementById("confirmFirmwareImage");
1044
+ confirmFirmwareImageButton.addEventListener("click", () => {
1045
+ device.confirmFirmwareImage(selectedImageIndex);
1046
+ });
1047
+ const updateConfirmFirmwareImageButton = () => {
1048
+ const enabled = device.firmwareStatus == "testing" || device.firmwareStatus == "uploaded";
1049
+ confirmFirmwareImageButton.disabled = !enabled;
1050
+ };
1051
+
1052
+ /** @type {HTMLButtonElement} */
1053
+ const eraseFirmwareImageButton = document.getElementById("eraseFirmwareImage");
1054
+ eraseFirmwareImageButton.addEventListener("click", () => {
1055
+ device.eraseFirmwareImage();
1056
+ });
1057
+ const updateEraseFirmwareImageButton = () => {
1058
+ const enabled = device.firmwareStatus == "uploaded";
1059
+ eraseFirmwareImageButton.disabled = !enabled;
1060
+ };
1061
+
1062
+ /** @type {HTMLSelectElement} */
1063
+ const imageSelectionSelect = document.getElementById("imageSelection");
1064
+ /** @type {HTMLOptGroupElement} */
1065
+ const imageSelectionOptGroup = imageSelectionSelect.querySelector("optgroup");
1066
+ device.addEventListener("firmwareImages", () => {
1067
+ imageSelectionOptGroup.innerHTML = "";
1068
+ device.firmwareImages.forEach((firmwareImage, index) => {
1069
+ const option = new Option(`${firmwareImage.version} (slot ${index})`, index);
1070
+ option.disabled = firmwareImage.empty;
1071
+ imageSelectionOptGroup.appendChild(option);
1072
+ });
1073
+ imageSelectionSelect.dispatchEvent(new Event("input"));
1074
+ });
1075
+ imageSelectionSelect.addEventListener("input", () => {
1076
+ selectedImageIndex = Number(imageSelectionSelect.value);
1077
+ console.log({ selectedImageIndex });
1078
+ });
1079
+ let selectedImageIndex = 0;
1080
+ device.addEventListener("isConnected", () => {
1081
+ imageSelectionSelect.disabled = !device.isConnected;
1082
+ });
1083
+
1084
+ function updateSelectImageSelect() {
1085
+ let enabled = true;
1086
+ switch (device.firmwareStatus) {
1087
+ case "uploading":
1088
+ case "erasing":
1089
+ enabled = false;
1090
+ break;
1091
+ }
1092
+ imageSelectionSelect.disabled = !enabled;
1093
+ }