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,925 @@
1
+ import * as BS from "../../build/brilliantsole.module.js";
2
+ window.BS = BS;
3
+ console.log({ BS });
4
+ //BS.setAllConsoleLevelFlags({ log: true });
5
+
6
+ const client = new BS.WebSocketClient();
7
+ console.log({ client });
8
+
9
+ window.client = client;
10
+
11
+ // SEARCH PARAMS
12
+
13
+ const url = new URL(location);
14
+ function setUrlParam(key, value) {
15
+ if (history.pushState) {
16
+ let searchParams = new URLSearchParams(window.location.search);
17
+ if (value) {
18
+ searchParams.set(key, value);
19
+ } else {
20
+ searchParams.delete(key);
21
+ }
22
+ let newUrl =
23
+ window.location.protocol + "//" + window.location.host + window.location.pathname + "?" + searchParams.toString();
24
+ window.history.pushState({ path: newUrl }, "", newUrl);
25
+ }
26
+ }
27
+ client.addEventListener("isConnected", () => {
28
+ if (client.isConnected) {
29
+ setUrlParam("webSocketUrl", client.webSocket.url);
30
+ webSocketUrlInput.value = client.webSocket.url;
31
+ } else {
32
+ setUrlParam("webSocketUrl");
33
+ }
34
+ });
35
+
36
+ // CONNECTION
37
+
38
+ /** @type {HTMLInputElement} */
39
+ const webSocketUrlInput = document.getElementById("webSocketUrl");
40
+ webSocketUrlInput.value = url.searchParams.get("webSocketUrl") || "";
41
+ client.addEventListener("isConnected", () => {
42
+ webSocketUrlInput.disabled = client.isConnected;
43
+ });
44
+
45
+ /** @type {HTMLButtonElement} */
46
+ const toggleConnectionButton = document.getElementById("toggleConnection");
47
+ toggleConnectionButton.addEventListener("click", () => {
48
+ if (client.isConnected) {
49
+ client.disconnect();
50
+ } else {
51
+ /** @type {string?} */
52
+ let webSocketUrl;
53
+ if (webSocketUrlInput.value.length > 0) {
54
+ webSocketUrl = webSocketUrlInput.value;
55
+ }
56
+ client.connect(webSocketUrl);
57
+ }
58
+ });
59
+ client.addEventListener("connectionStatus", () => {
60
+ switch (client.connectionStatus) {
61
+ case "connected":
62
+ case "notConnected":
63
+ toggleConnectionButton.disabled = false;
64
+ toggleConnectionButton.innerText = client.isConnected ? "disconnect" : "connect";
65
+ break;
66
+ case "connecting":
67
+ case "disconnecting":
68
+ toggleConnectionButton.innerText = client.connectionStatus;
69
+ toggleConnectionButton.disabled = true;
70
+ break;
71
+ }
72
+ });
73
+
74
+ // SCANNER
75
+
76
+ /** @type {HTMLInputElement} */
77
+ const isScanningAvailableCheckbox = document.getElementById("isScanningAvailable");
78
+ client.addEventListener("isScanningAvailable", () => {
79
+ isScanningAvailableCheckbox.checked = client.isScanningAvailable;
80
+ });
81
+
82
+ /** @type {HTMLButtonElement} */
83
+ const toggleScanButton = document.getElementById("toggleScan");
84
+ toggleScanButton.addEventListener("click", () => {
85
+ client.toggleScan();
86
+ });
87
+ client.addEventListener("isScanningAvailable", () => {
88
+ toggleScanButton.disabled = !client.isScanningAvailable;
89
+ });
90
+ client.addEventListener("isScanning", () => {
91
+ toggleScanButton.innerText = client.isScanning ? "stop scanning" : "scan";
92
+ });
93
+
94
+ // DISCOVERED DEVICES
95
+
96
+ /** @type {HTMLTemplateElement} */
97
+ const discoveredDeviceTemplate = document.getElementById("discoveredDeviceTemplate");
98
+ const discoveredDevicesContainer = document.getElementById("discoveredDevices");
99
+ /** @type {Object.<string, HTMLElement>} */
100
+ let discoveredDeviceContainers = {};
101
+
102
+ client.addEventListener("discoveredDevice", (event) => {
103
+ const discoveredDevice = event.message.discoveredDevice;
104
+
105
+ let discoveredDeviceContainer = discoveredDeviceContainers[discoveredDevice.bluetoothId];
106
+ if (!discoveredDeviceContainer) {
107
+ discoveredDeviceContainer = discoveredDeviceTemplate.content.cloneNode(true).querySelector(".discoveredDevice");
108
+
109
+ /** @type {HTMLButtonElement} */
110
+ const toggleConnectionButton = discoveredDeviceContainer.querySelector(".toggleConnection");
111
+ toggleConnectionButton.addEventListener("click", () => {
112
+ let device = client.devices[discoveredDevice.bluetoothId];
113
+ if (device) {
114
+ device.toggleConnection();
115
+ } else {
116
+ device = client.connectToDevice(discoveredDevice.bluetoothId);
117
+ onDevice(device);
118
+ }
119
+ });
120
+
121
+ /** @param {BS.Device} device */
122
+ const onDevice = (device) => {
123
+ device.addEventListener("connectionStatus", () => {
124
+ updateToggleConnectionButton(device);
125
+ });
126
+ updateToggleConnectionButton(device);
127
+ };
128
+
129
+ discoveredDeviceContainer._onDevice = onDevice;
130
+
131
+ /** @param {BS.Device} device */
132
+ const updateToggleConnectionButton = (device) => {
133
+ console.log({ deviceConnectionStatus: device.connectionStatus });
134
+ switch (device.connectionStatus) {
135
+ case "connected":
136
+ case "notConnected":
137
+ toggleConnectionButton.innerText = device.isConnected ? "disconnect" : "connect";
138
+ toggleConnectionButton.disabled = false;
139
+ break;
140
+ case "connecting":
141
+ case "disconnecting":
142
+ toggleConnectionButton.innerText = device.connectionStatus;
143
+ toggleConnectionButton.disabled = true;
144
+ break;
145
+ }
146
+ };
147
+
148
+ discoveredDeviceContainers[discoveredDevice.bluetoothId] = discoveredDeviceContainer;
149
+ discoveredDevicesContainer.appendChild(discoveredDeviceContainer);
150
+ }
151
+
152
+ updateDiscoveredDeviceContainer(discoveredDevice);
153
+ });
154
+
155
+ /** @param {BS.DiscoveredDevice} discoveredDevice */
156
+ function updateDiscoveredDeviceContainer(discoveredDevice) {
157
+ const discoveredDeviceContainer = discoveredDeviceContainers[discoveredDevice.bluetoothId];
158
+ if (!discoveredDeviceContainer) {
159
+ console.warn(`no discoveredDeviceContainer for device id ${discoveredDevice.bluetoothId}`);
160
+ return;
161
+ }
162
+ discoveredDeviceContainer.querySelector(".name").innerText = discoveredDevice.name;
163
+ discoveredDeviceContainer.querySelector(".rssi").innerText = discoveredDevice.rssi;
164
+ discoveredDeviceContainer.querySelector(".deviceType").innerText = discoveredDevice.deviceType;
165
+ }
166
+
167
+ /** @param {BS.DiscoveredDevice} discoveredDevice */
168
+ function removeDiscoveredDeviceContainer(discoveredDevice) {
169
+ const discoveredDeviceContainer = discoveredDeviceContainers[discoveredDevice.bluetoothId];
170
+ if (!discoveredDeviceContainer) {
171
+ console.warn(`no discoveredDeviceContainer for device id ${discoveredDevice.bluetoothId}`);
172
+ return;
173
+ }
174
+
175
+ discoveredDeviceContainer.remove();
176
+ delete discoveredDeviceContainers[discoveredDevice.bluetoothId];
177
+ }
178
+
179
+ client.addEventListener("expiredDiscoveredDevice", (event) => {
180
+ const discoveredDevice = event.message.discoveredDevice;
181
+ removeDiscoveredDeviceContainer(discoveredDevice);
182
+ });
183
+
184
+ function clearDiscoveredDevices() {
185
+ discoveredDevicesContainer.innerHTML = "";
186
+ discoveredDeviceContainers = {};
187
+ }
188
+
189
+ client.addEventListener("notConnected", () => {
190
+ clearDiscoveredDevices();
191
+ });
192
+
193
+ client.addEventListener("isScanning", () => {
194
+ if (client.isScanning) {
195
+ clearDiscoveredDevices();
196
+ }
197
+ });
198
+
199
+ BS.DeviceManager.AddEventListener("deviceIsConnected", (event) => {
200
+ const device = event.message.device;
201
+ console.log("deviceIsConnected", device);
202
+ const discoveredDeviceContainer = discoveredDeviceContainers[device.bluetoothId];
203
+ if (!discoveredDeviceContainer) {
204
+ return;
205
+ }
206
+ discoveredDeviceContainer._onDevice(device);
207
+ });
208
+
209
+ // AVAILABLE DEVICES
210
+
211
+ /** @type {HTMLTemplateElement} */
212
+ const connectedDeviceTemplate = document.getElementById("connectedDeviceTemplate");
213
+ const connectedDevicesContainer = document.getElementById("connectedDevices");
214
+ /** @type {Object.<string, HTMLElement>} */
215
+ let connectedDeviceContainers = {};
216
+
217
+ BS.DeviceManager.AddEventListener("connectedDevices", (event) => {
218
+ const { connectedDevices } = event.message;
219
+ console.log({ connectedDevices });
220
+
221
+ connectedDevices.forEach((device) => {
222
+ if (device.connectionType != "client" || !device.bluetoothId) {
223
+ return;
224
+ }
225
+ let connectedDeviceContainer = connectedDeviceContainers[device.bluetoothId];
226
+ if (!connectedDeviceContainer) {
227
+ connectedDeviceContainer = connectedDeviceTemplate.content.cloneNode(true).querySelector(".connectedDevice");
228
+ connectedDeviceContainers[device.bluetoothId] = connectedDeviceContainer;
229
+
230
+ /** @type {HTMLPreElement} */
231
+ const deviceInformationPre = connectedDeviceContainer.querySelector(".deviceInformation");
232
+ const setDeviceInformationPre = () =>
233
+ (deviceInformationPre.textContent = JSON.stringify(device.deviceInformation, null, 2));
234
+ setDeviceInformationPre();
235
+ device.addEventListener("deviceInformation", () => setDeviceInformationPre());
236
+
237
+ /** @type {HTMLSpanElement} */
238
+ const batteryLevelSpan = connectedDeviceContainer.querySelector(".batteryLevel");
239
+ const setBatteryLevelSpan = () => (batteryLevelSpan.innerText = device.batteryLevel);
240
+ setBatteryLevelSpan();
241
+ device.addEventListener("batteryLevel", () => setBatteryLevelSpan());
242
+
243
+ /** @type {HTMLSpanElement} */
244
+ const nameSpan = connectedDeviceContainer.querySelector(".name");
245
+ const setNameSpan = () => (nameSpan.innerText = device.name);
246
+ setNameSpan();
247
+ device.addEventListener("getName", () => setNameSpan());
248
+
249
+ /** @type {HTMLInputElement} */
250
+ const setNameInput = connectedDeviceContainer.querySelector(".setNameInput");
251
+ setNameInput.minLength = BS.MinNameLength;
252
+ setNameInput.maxLength = BS.MaxNameLength;
253
+ setNameInput.disabled = !device.isConnected;
254
+
255
+ /** @type {HTMLButtonElement} */
256
+ const setNameButton = connectedDeviceContainer.querySelector(".setNameButton");
257
+ setNameButton.disabled = !device.isConnected;
258
+
259
+ device.addEventListener("isConnected", () => {
260
+ setNameInput.disabled = !device.isConnected;
261
+ });
262
+ device.addEventListener("notConnected", () => {
263
+ setNameInput.value = "";
264
+ });
265
+
266
+ setNameInput.addEventListener("input", () => {
267
+ setNameButton.disabled = setNameInput.value.length < device.minNameLength;
268
+ });
269
+
270
+ setNameButton.addEventListener("click", () => {
271
+ console.log(`setting name to ${setNameInput.value}`);
272
+ device.setName(setNameInput.value);
273
+ setNameInput.value = "";
274
+ setNameButton.disabled = true;
275
+ });
276
+
277
+ /** @type {HTMLSpanElement} */
278
+ const deviceTypeSpan = connectedDeviceContainer.querySelector(".deviceType");
279
+ const setDeviceTypeSpan = () => (deviceTypeSpan.innerText = device.type);
280
+ setDeviceTypeSpan();
281
+ device.addEventListener("getType", () => setDeviceTypeSpan());
282
+
283
+ /** @type {HTMLButtonElement} */
284
+ const setTypeButton = connectedDeviceContainer.querySelector(".setTypeButton");
285
+
286
+ /** @type {HTMLSelectElement} */
287
+ const setTypeSelect = connectedDeviceContainer.querySelector(".setTypeSelect");
288
+ /** @type {HTMLOptGroupElement} */
289
+ const setTypeSelectOptgroup = setTypeSelect.querySelector("optgroup");
290
+ BS.DeviceTypes.forEach((type) => {
291
+ setTypeSelectOptgroup.appendChild(new Option(type));
292
+ });
293
+
294
+ device.addEventListener("isConnected", () => {
295
+ setTypeSelect.disabled = !device.isConnected;
296
+ });
297
+ setTypeSelect.disabled = !device.isConnected;
298
+
299
+ device.addEventListener("getType", () => {
300
+ setTypeSelect.value = device.type;
301
+ });
302
+
303
+ setTypeSelect.addEventListener("input", () => {
304
+ setTypeButton.disabled = setTypeSelect.value == device.type;
305
+ });
306
+ setTypeSelect.value = device.type;
307
+
308
+ setTypeButton.addEventListener("click", () => {
309
+ device.setType(setTypeSelect.value);
310
+ setTypeButton.disabled = true;
311
+ });
312
+
313
+ /** @type {HTMLPreElement} */
314
+ const sensorConfigurationPre = connectedDeviceContainer.querySelector(".sensorConfiguration");
315
+ const setSensorConfigurationPre = () =>
316
+ (sensorConfigurationPre.textContent = JSON.stringify(device.sensorConfiguration, null, 2));
317
+ setSensorConfigurationPre();
318
+ device.addEventListener("getSensorConfiguration", () => setSensorConfigurationPre());
319
+
320
+ /** @type {HTMLTemplateElement} */
321
+ const sensorTypeConfigurationTemplate = connectedDeviceContainer.querySelector(
322
+ ".sensorTypeConfigurationTemplate"
323
+ );
324
+ device.sensorTypes.forEach((sensorType) => {
325
+ const sensorTypeConfigurationContainer = sensorTypeConfigurationTemplate.content
326
+ .cloneNode(true)
327
+ .querySelector(".sensorTypeConfiguration");
328
+ sensorTypeConfigurationContainer.querySelector(".sensorType").innerText = sensorType;
329
+
330
+ /** @type {HTMLInputElement} */
331
+ const sensorRateInput = sensorTypeConfigurationContainer.querySelector(".sensorRate");
332
+ sensorRateInput.value = 0;
333
+ sensorRateInput.max = BS.MaxSensorRate;
334
+ sensorRateInput.step = BS.SensorRateStep;
335
+ sensorRateInput.addEventListener("input", () => {
336
+ const sensorRate = Number(sensorRateInput.value);
337
+ console.log({ sensorType, sensorRate });
338
+ device.setSensorConfiguration({ [sensorType]: sensorRate });
339
+ });
340
+ sensorRateInput.disabled = !device.isConnected;
341
+
342
+ sensorTypeConfigurationTemplate.parentElement.insertBefore(
343
+ sensorTypeConfigurationContainer,
344
+ sensorTypeConfigurationTemplate
345
+ );
346
+ sensorTypeConfigurationContainer.dataset.sensorType = sensorType;
347
+ });
348
+
349
+ device.addEventListener("isConnected", () => {
350
+ connectedDeviceContainer.querySelectorAll("input").forEach((input) => (input.disabled = !device.isConnected));
351
+ });
352
+
353
+ device.addEventListener("getSensorConfiguration", () => {
354
+ for (const sensorType in device.sensorConfiguration) {
355
+ connectedDeviceContainer.querySelector(
356
+ `.sensorTypeConfiguration[data-sensor-type="${sensorType}"] input`
357
+ ).value = device.sensorConfiguration[sensorType];
358
+ }
359
+ });
360
+
361
+ /** @type {HTMLPreElement} */
362
+ const sensorDataPre = connectedDeviceContainer.querySelector(".sensorData");
363
+ const setSensorDataPre = (event) => (sensorDataPre.textContent = JSON.stringify(event.message, null, 2));
364
+ device.addEventListener("sensorData", (event) => setSensorDataPre(event));
365
+
366
+ /** @type {HTMLButtonElement} */
367
+ const triggerVibrationButton = connectedDeviceContainer.querySelector(".triggerVibration");
368
+ triggerVibrationButton.addEventListener("click", () => {
369
+ device.triggerVibration([
370
+ {
371
+ type: "waveformEffect",
372
+ segments: [{ effect: "doubleClick100" }],
373
+ },
374
+ ]);
375
+ });
376
+ device.addEventListener("isConnected", () => {
377
+ triggerVibrationButton.disabled = !device.isConnected;
378
+ });
379
+ triggerVibrationButton.disabled = !device.isConnected;
380
+
381
+ /** @type {HTMLButtonElement} */
382
+ const toggleConnectionButton = connectedDeviceContainer.querySelector(".toggleConnection");
383
+ toggleConnectionButton.addEventListener("click", () => {
384
+ device.toggleConnection();
385
+ });
386
+ const updateToggleConnectionButton = () => {
387
+ switch (device.connectionStatus) {
388
+ case "connected":
389
+ case "notConnected":
390
+ toggleConnectionButton.disabled = false;
391
+ toggleConnectionButton.innerText = device.isConnected ? "disconnect" : "connect";
392
+ break;
393
+ case "connecting":
394
+ case "disconnecting":
395
+ toggleConnectionButton.innerText = device.connectionStatus;
396
+ toggleConnectionButton.disabled = true;
397
+ break;
398
+ }
399
+ };
400
+ updateToggleConnectionButton();
401
+ device.addEventListener("connectionStatus", () => updateToggleConnectionButton());
402
+
403
+ /** @type {File?} */
404
+ let file;
405
+
406
+ /** @type {HTMLInputElement} */
407
+ const fileInput = connectedDeviceContainer.querySelector(".file");
408
+ fileInput.addEventListener("input", () => {
409
+ if (fileInput.files[0].size > device.maxFileLength) {
410
+ console.log("file size too large");
411
+ return;
412
+ }
413
+ file = fileInput.files[0];
414
+ console.log("file", file);
415
+ updateToggleFileTransferButton();
416
+ });
417
+
418
+ const maxFileLengthSpan = connectedDeviceContainer.querySelector(".maxFileLength");
419
+ const updateMaxFileLengthSpan = () => {
420
+ maxFileLengthSpan.innerText = (device.maxFileLength / 1024).toLocaleString();
421
+ };
422
+ updateMaxFileLengthSpan();
423
+ device.addEventListener("isConnected", () => {
424
+ updateMaxFileLengthSpan();
425
+ });
426
+
427
+ /** @type {BS.FileType} */
428
+ let fileType;
429
+
430
+ /** @type {HTMLSelectElement} */
431
+ const fileTransferTypesSelect = connectedDeviceContainer.querySelector(".fileTransferTypes");
432
+ fileTransferTypesSelect.addEventListener("input", () => {
433
+ fileType = fileTransferTypesSelect.value;
434
+ console.log({ fileType });
435
+ switch (fileType) {
436
+ case "tflite":
437
+ fileInput.accept = ".tflite";
438
+ break;
439
+ }
440
+ });
441
+ /** @type {HTMLOptGroupElement} */
442
+ const fileTransferTypesOptgroup = fileTransferTypesSelect.querySelector("optgroup");
443
+ BS.FileTypes.forEach((fileType) => {
444
+ fileTransferTypesOptgroup.appendChild(new Option(fileType));
445
+ });
446
+ fileTransferTypesSelect.dispatchEvent(new Event("input"));
447
+
448
+ /** @type {HTMLProgressElement} */
449
+ const fileTransferProgress = connectedDeviceContainer.querySelector(".fileTransferProgress");
450
+ console.log("fileTransferProgress", fileTransferProgress);
451
+
452
+ device.addEventListener("fileTransferProgress", (event) => {
453
+ const progress = event.message.progress;
454
+ console.log("fileTransferProgress", progress, fileTransferProgress);
455
+ fileTransferProgress.value = progress == 1 ? 0 : progress;
456
+ });
457
+ device.addEventListener("fileTransferStatus", () => {
458
+ if (device.fileTransferStatus == "idle") {
459
+ fileTransferProgress.value = 0;
460
+ }
461
+ });
462
+
463
+ /** @type {HTMLButtonElement} */
464
+ const toggleFileTransferButton = connectedDeviceContainer.querySelector(".toggleFileTransfer");
465
+ toggleFileTransferButton.addEventListener("click", async () => {
466
+ if (device.fileTransferStatus == "idle") {
467
+ if (fileTransferDirection == "send") {
468
+ if (fileType == "tflite") {
469
+ await device.setTfliteName(file.name.replaceAll(".tflite", ""));
470
+ }
471
+ device.sendFile(fileType, file);
472
+ } else {
473
+ device.receiveFile(fileType);
474
+ }
475
+ } else {
476
+ device.cancelFileTransfer();
477
+ }
478
+ });
479
+ const updateToggleFileTransferButton = () => {
480
+ const enabled = device.isConnected && (file || fileTransferDirection == "receive");
481
+ toggleFileTransferButton.disabled = !enabled;
482
+
483
+ /** @type {String} */
484
+ let innerText;
485
+ switch (device.fileTransferStatus) {
486
+ case "idle":
487
+ innerText = `${fileTransferDirection} file`;
488
+ break;
489
+ case "sending":
490
+ innerText = "stop sending file";
491
+ break;
492
+ case "receiving":
493
+ innerText = "stop receiving file";
494
+ break;
495
+ }
496
+ toggleFileTransferButton.innerText = innerText;
497
+ };
498
+ device.addEventListener("isConnected", () => {
499
+ updateToggleFileTransferButton();
500
+ });
501
+ device.addEventListener("fileTransferStatus", () => {
502
+ updateToggleFileTransferButton();
503
+ });
504
+
505
+ /** @type {BS.FileTransferDirection} */
506
+ let fileTransferDirection;
507
+ /** @type {HTMLSelectElement} */
508
+ const fileTransferDirectionSelect = connectedDeviceContainer.querySelector(".fileTransferDirection");
509
+ fileTransferDirectionSelect.addEventListener("input", () => {
510
+ fileTransferDirection = fileTransferDirectionSelect.value;
511
+ console.log({ fileTransferDirection });
512
+ updateToggleFileTransferButton();
513
+ });
514
+ fileTransferDirectionSelect.dispatchEvent(new Event("input"));
515
+
516
+ /** @param {File} file */
517
+ function downloadFile(file) {
518
+ const a = document.createElement("a");
519
+ document.body.appendChild(a);
520
+ a.style = "display: none";
521
+ const url = window.URL.createObjectURL(file);
522
+ a.href = url;
523
+ a.download = file.name;
524
+ a.click();
525
+ window.URL.revokeObjectURL(url);
526
+ }
527
+
528
+ device.addEventListener("fileReceived", (event) => {
529
+ const file = event.message.file;
530
+ downloadFile(file);
531
+ });
532
+
533
+ // TFLITE
534
+
535
+ /** @type {HTMLSpanElement} */
536
+ const tfliteNameSpan = connectedDeviceContainer.querySelector(".tfliteName");
537
+ /** @type {HTMLInputElement} */
538
+ const setTfliteNameInput = connectedDeviceContainer.querySelector(".setTfliteNameInput");
539
+ /** @type {HTMLButtonElement} */
540
+ const setTfliteNameButton = connectedDeviceContainer.querySelector(".setTfliteNameButton");
541
+
542
+ device.addEventListener("getTfliteName", () => {
543
+ tfliteNameSpan.innerText = device.tfliteName;
544
+
545
+ setTfliteNameButton.innerText = "set name";
546
+ setTfliteNameButton.disabled = !device.isConnected;
547
+
548
+ setTfliteNameInput.value = "";
549
+ setTfliteNameInput.disabled = !device.isConnected;
550
+ });
551
+ tfliteNameSpan.innerText = device.tfliteName;
552
+
553
+ setTfliteNameButton.addEventListener("click", () => {
554
+ device.setTfliteName(setTfliteNameInput.value);
555
+
556
+ setTfliteNameInput.disabled = true;
557
+
558
+ setTfliteNameButton.innerText = "setting name...";
559
+ setTfliteNameButton.disabled = true;
560
+ });
561
+
562
+ /** @type {HTMLSpanElement} */
563
+ const tfliteTaskSpan = connectedDeviceContainer.querySelector(".tfliteTask");
564
+ /** @type {HTMLSelectElement} */
565
+ const setTfliteTaskSelect = connectedDeviceContainer.querySelector(".setTfliteTaskSelect");
566
+ /** @type {HTMLOptGroupElement} */
567
+ const setTfliteTaskOptgroup = setTfliteTaskSelect.querySelector("optgroup");
568
+ /** @type {HTMLButtonElement} */
569
+ const setTfliteTaskButton = connectedDeviceContainer.querySelector(".setTfliteTaskButton");
570
+
571
+ BS.TfliteTasks.forEach((task) => {
572
+ setTfliteTaskOptgroup.appendChild(new Option(task));
573
+ });
574
+
575
+ device.addEventListener("getTfliteTask", () => {
576
+ const task = device.tfliteTask;
577
+ setTfliteTaskSelect.value = task;
578
+ tfliteTaskSpan.innerText = task;
579
+ });
580
+ setTfliteTaskSelect.value = device.tfliteTask;
581
+ tfliteTaskSpan.innerText = device.tfliteTask;
582
+
583
+ setTfliteTaskButton.addEventListener("click", () => {
584
+ device.setTfliteTask(setTfliteTaskSelect.value);
585
+ });
586
+ device.addEventListener("getTfliteInferencingEnabled", () => {
587
+ setTfliteTaskButton.disabled = device.tfliteInferencingEnabled;
588
+ });
589
+
590
+ /** @type {HTMLSpanElement} */
591
+ const tfliteSampleRateSpan = connectedDeviceContainer.querySelector(".tfliteSampleRate");
592
+ /** @type {HTMLInputElement} */
593
+ const setTfliteSampleRateInput = connectedDeviceContainer.querySelector(".setTfliteSampleRateInput");
594
+ /** @type {HTMLButtonElement} */
595
+ const setTfliteSampleRateButton = connectedDeviceContainer.querySelector(".setTfliteSampleRateButton");
596
+
597
+ device.addEventListener("getTfliteSampleRate", () => {
598
+ tfliteSampleRateSpan.innerText = device.tfliteSampleRate;
599
+
600
+ setTfliteSampleRateInput.value = "";
601
+ setTfliteSampleRateInput.disabled = false;
602
+
603
+ setTfliteSampleRateButton.disabled = false;
604
+ setTfliteSampleRateButton.innerText = "set sample rate";
605
+ });
606
+ tfliteSampleRateSpan.innerText = device.tfliteSampleRate;
607
+
608
+ setTfliteSampleRateButton.addEventListener("click", () => {
609
+ device.setTfliteSampleRate(Number(setTfliteSampleRateInput.value));
610
+
611
+ setTfliteSampleRateInput.disabled = true;
612
+
613
+ setTfliteSampleRateButton.disabled = true;
614
+ setTfliteSampleRateButton.innerText = "setting sample rate...";
615
+ });
616
+ device.addEventListener("getTfliteInferencingEnabled", () => {
617
+ setTfliteSampleRateButton.disabled = device.tfliteInferencingEnabled;
618
+ });
619
+
620
+ const tfliteSensorTypesContainer = connectedDeviceContainer.querySelector(".tfliteSensorTypes");
621
+ /** @type {HTMLTemplateElement} */
622
+ const tfliteSensorTypeTemplate = connectedDeviceContainer.querySelector(".tfliteSensorTypeTemplate");
623
+ /** @type {Object.<string, HTMLElement>} */
624
+ const tfliteSensorTypeContainers = {};
625
+ /** @type {BS.SensorType[]} */
626
+ let tfliteSensorTypes = [];
627
+ /** @type {HTMLButtonElement} */
628
+ const setTfliteSensorTypesButton = connectedDeviceContainer.querySelector(".setTfliteSensorTypes");
629
+
630
+ BS.TfliteSensorTypes.forEach((sensorType) => {
631
+ const sensorTypeContainer = tfliteSensorTypeTemplate.content.cloneNode(true).querySelector(".sensorType");
632
+ sensorTypeContainer.querySelector(".name").innerText = sensorType;
633
+
634
+ /** @type {HTMLInputElement} */
635
+ const isSensorEnabledInput = sensorTypeContainer.querySelector(".enabled");
636
+ isSensorEnabledInput.addEventListener("input", () => {
637
+ if (isSensorEnabledInput.checked) {
638
+ tfliteSensorTypes.push(sensorType);
639
+ } else {
640
+ tfliteSensorTypes.splice(tfliteSensorTypes.indexOf(sensorType), 1);
641
+ }
642
+ console.log("tfliteSensorTypes", tfliteSensorTypes);
643
+ });
644
+
645
+ device.addEventListener("getTfliteSensorTypes", () => {
646
+ isSensorEnabledInput.checked = device.tfliteSensorTypes.includes(sensorType);
647
+ });
648
+ isSensorEnabledInput.checked = device.tfliteSensorTypes.includes(sensorType);
649
+
650
+ tfliteSensorTypeContainers[sensorType] = sensorTypeContainer;
651
+
652
+ tfliteSensorTypesContainer.appendChild(sensorTypeContainer);
653
+ });
654
+
655
+ device.addEventListener("getTfliteSensorTypes", () => {
656
+ tfliteSensorTypes = device.tfliteSensorTypes;
657
+ });
658
+
659
+ setTfliteSensorTypesButton.addEventListener("click", () => {
660
+ setTfliteSensorTypesButton.disabled = true;
661
+ setTfliteSensorTypesButton.innerText = "setting sensor types...";
662
+ device.setTfliteSensorTypes(tfliteSensorTypes);
663
+ });
664
+ device.addEventListener("getTfliteSensorTypes", () => {
665
+ setTfliteSensorTypesButton.disabled = false;
666
+ setTfliteSensorTypesButton.innerText = "set sensor types";
667
+ });
668
+ device.addEventListener("getTfliteInferencingEnabled", () => {
669
+ setTfliteSensorTypesButton.disabled = device.tfliteInferencingEnabled;
670
+ });
671
+ setTfliteSensorTypesButton.disabled = false;
672
+
673
+ /** @type {HTMLInputElement} */
674
+ const setTfliteIsReadyInput = connectedDeviceContainer.querySelector(".tfliteIsReady");
675
+ device.addEventListener("tfliteIsReady", () => {
676
+ setTfliteIsReadyInput.checked = device.tfliteIsReady;
677
+ });
678
+ setTfliteIsReadyInput.checked = device.tfliteIsReady;
679
+
680
+ /** @type {HTMLSpanElement} */
681
+ const tfliteThresholdSpan = connectedDeviceContainer.querySelector(".tfliteThreshold");
682
+ /** @type {HTMLInputElement} */
683
+ const setTfliteThresholdInput = connectedDeviceContainer.querySelector(".setTfliteThresholdInput");
684
+ /** @type {HTMLButtonElement} */
685
+ const setTfliteThresholdButton = connectedDeviceContainer.querySelector(".setTfliteThresholdButton");
686
+
687
+ device.addEventListener("getTfliteThreshold", () => {
688
+ tfliteThresholdSpan.innerText = device.tfliteThreshold;
689
+
690
+ setTfliteThresholdInput.value = "";
691
+ setTfliteThresholdInput.disabled = false;
692
+
693
+ setTfliteThresholdButton.disabled = false;
694
+ setTfliteThresholdButton.innerText = "set threshold";
695
+ });
696
+ tfliteThresholdSpan.innerText = device.tfliteThreshold;
697
+
698
+ setTfliteThresholdButton.addEventListener("click", () => {
699
+ device.setTfliteThreshold(Number(setTfliteThresholdInput.value));
700
+
701
+ setTfliteThresholdInput.disabled = true;
702
+
703
+ setTfliteThresholdButton.disabled = true;
704
+ setTfliteThresholdButton.innerText = "setting threshold...";
705
+ });
706
+
707
+ /** @type {HTMLSpanElement} */
708
+ const tfliteCaptureDelaySpan = connectedDeviceContainer.querySelector(".tfliteCaptureDelay");
709
+ /** @type {HTMLInputElement} */
710
+ const setTfliteCaptureDelayInput = connectedDeviceContainer.querySelector(".setTfliteCaptureDelayInput");
711
+ /** @type {HTMLButtonElement} */
712
+ const setTfliteCaptureDelayButton = connectedDeviceContainer.querySelector(".setTfliteCaptureDelayButton");
713
+
714
+ device.addEventListener("getTfliteCaptureDelay", () => {
715
+ tfliteCaptureDelaySpan.innerText = device.tfliteCaptureDelay;
716
+
717
+ setTfliteCaptureDelayInput.value = "";
718
+ setTfliteCaptureDelayInput.disabled = false;
719
+
720
+ setTfliteCaptureDelayButton.disabled = false;
721
+ setTfliteCaptureDelayButton.innerText = "set capture delay";
722
+ });
723
+ tfliteCaptureDelaySpan.innerText = device.tfliteCaptureDelay;
724
+
725
+ setTfliteCaptureDelayButton.addEventListener("click", () => {
726
+ device.setTfliteCaptureDelay(Number(setTfliteCaptureDelayInput.value));
727
+
728
+ setTfliteCaptureDelayInput.disabled = true;
729
+
730
+ setTfliteCaptureDelayButton.disabled = true;
731
+ setTfliteCaptureDelayButton.innerText = "setting capture delay...";
732
+ });
733
+
734
+ /** @type {HTMLInputElement} */
735
+ const tfliteInferencingEnabledInput = connectedDeviceContainer.querySelector(".tfliteInferencingEnabled");
736
+ /** @type {HTMLButtonElement} */
737
+ const toggleTfliteInferencingEnabledButton = connectedDeviceContainer.querySelector(
738
+ ".toggleTfliteInferencingEnabled"
739
+ );
740
+
741
+ const updateTfliteInferencingUI = () => {
742
+ toggleTfliteInferencingEnabledButton.disabled = !device.tfliteIsReady;
743
+
744
+ tfliteInferencingEnabledInput.checked = device.tfliteInferencingEnabled;
745
+ toggleTfliteInferencingEnabledButton.innerText = device.tfliteInferencingEnabled
746
+ ? "disable inferencing"
747
+ : "enable inferencing";
748
+ };
749
+
750
+ device.addEventListener("tfliteIsReady", () => {
751
+ toggleTfliteInferencingEnabledButton.disabled = !device.tfliteIsReady;
752
+ });
753
+ device.addEventListener("getTfliteInferencingEnabled", () => {
754
+ tfliteInferencingEnabledInput.checked = device.tfliteInferencingEnabled;
755
+ toggleTfliteInferencingEnabledButton.innerText = device.tfliteInferencingEnabled
756
+ ? "disable inferencing"
757
+ : "enable inferencing";
758
+ });
759
+ updateTfliteInferencingUI();
760
+
761
+ toggleTfliteInferencingEnabledButton.addEventListener("click", () => {
762
+ device.toggleTfliteInferencing();
763
+ });
764
+
765
+ /** @type {HTMLPreElement} */
766
+ const tfliteInferencePre = connectedDeviceContainer.querySelector(".tfliteInference");
767
+ device.addEventListener("tfliteInference", (event) => {
768
+ console.log("inference", event.message.tfliteInference);
769
+ tfliteInferencePre.textContent = JSON.stringify(event.message.tfliteInference, null, 2);
770
+ });
771
+
772
+ const updateTfliteUI = () => {
773
+ const disabled = !device.isConnected;
774
+ setTfliteCaptureDelayInput.disabled = disabled;
775
+ setTfliteCaptureDelayButton.disabled = disabled;
776
+
777
+ setTfliteTaskSelect.disabled = disabled;
778
+ setTfliteTaskButton.disabled = disabled;
779
+
780
+ setTfliteThresholdInput.disabled = disabled;
781
+ setTfliteThresholdButton.disabled = disabled;
782
+
783
+ setTfliteNameInput.disabled = disabled;
784
+ setTfliteNameButton.disabled = disabled;
785
+
786
+ setTfliteSampleRateInput.disabled = disabled;
787
+ setTfliteSampleRateButton.disabled = disabled;
788
+ };
789
+ device.addEventListener("isConnected", () => {
790
+ updateTfliteUI();
791
+ });
792
+ updateTfliteUI();
793
+
794
+ // FIRMWARE
795
+
796
+ /** @type {File?} */
797
+ let firmware;
798
+
799
+ /** @type {HTMLInputElement} */
800
+ const firmwareInput = connectedDeviceContainer.querySelector(".firmwareInput");
801
+ firmwareInput.addEventListener("input", () => {
802
+ firmware = firmwareInput.files[0];
803
+ updateToggleFirmwareUploadButton();
804
+ });
805
+ /** @type {HTMLButtonElement} */
806
+ const toggleFirmwareUploadButton = connectedDeviceContainer.querySelector(".toggleFirmwareUpload");
807
+ toggleFirmwareUploadButton.addEventListener("click", () => {
808
+ device.uploadFirmware(firmware);
809
+ });
810
+ const updateToggleFirmwareUploadButton = () => {
811
+ const enabled = device.isConnected && Boolean(firmware);
812
+ toggleFirmwareUploadButton.disabled = !enabled;
813
+ };
814
+ device.addEventListener("isConnected", () => {
815
+ updateToggleFirmwareUploadButton();
816
+ });
817
+
818
+ /** @type {HTMLProgressElement} */
819
+ const firmwareUploadProgress = connectedDeviceContainer.querySelector(".firmwareUploadProgress");
820
+ /** @type {HTMLSpanElement} */
821
+ const firmwareUploadProgressPercentageSpan = connectedDeviceContainer.querySelector(
822
+ ".firmwareUploadProgressPercentage"
823
+ );
824
+ device.addEventListener("firmwareUploadProgress", (event) => {
825
+ const progress = event.message.firmwareUploadProgress;
826
+ firmwareUploadProgress.value = progress;
827
+ firmwareUploadProgressPercentageSpan.innerText = `${Math.floor(100 * progress)}%`;
828
+ });
829
+ device.addEventListener("firmwareUploadComplete", () => {
830
+ firmwareUploadProgress.value = 0;
831
+ });
832
+ device.addEventListener("firmwareStatus", () => {
833
+ const isUploading = device.firmwareStatus == "uploading";
834
+ firmwareUploadProgressPercentageSpan.style.display = isUploading ? "" : "none";
835
+ });
836
+
837
+ /** @type {HTMLPreElement} */
838
+ const firmwareImagesPre = connectedDeviceContainer.querySelector(".firmwareImages");
839
+ device.addEventListener("firmwareImages", () => {
840
+ firmwareImagesPre.textContent = JSON.stringify(
841
+ device.firmwareImages,
842
+ (key, value) => (key == "hash" ? Array.from(value).join(",") : value),
843
+ 2
844
+ );
845
+ });
846
+
847
+ device.addEventListener("isConnected", () => {
848
+ if (device.isConnected) {
849
+ device.getFirmwareImages();
850
+ }
851
+ });
852
+
853
+ /** @type {HTMLSpanElement} */
854
+ const firmwareStatusSpan = connectedDeviceContainer.querySelector(".firmwareStatus");
855
+
856
+ /** @type {HTMLButtonElement} */
857
+ const resetButton = connectedDeviceContainer.querySelector(".reset");
858
+ resetButton.addEventListener("click", () => {
859
+ device.reset();
860
+ resetButton.disabled = true;
861
+ });
862
+ const updateResetButton = () => {
863
+ const status = device.firmwareStatus;
864
+ const enabled = status == "pending" || status == "testing";
865
+ resetButton.disabled = !enabled;
866
+ };
867
+
868
+ /** @type {HTMLButtonElement} */
869
+ const testFirmwareImageButton = connectedDeviceContainer.querySelector(".testFirmwareImage");
870
+ testFirmwareImageButton.addEventListener("click", () => {
871
+ device.testFirmwareImage();
872
+ });
873
+ const updateTestFirmwareImageButton = () => {
874
+ const enabled = device.firmwareStatus == "uploaded";
875
+ testFirmwareImageButton.disabled = !enabled;
876
+ };
877
+
878
+ /** @type {HTMLButtonElement} */
879
+ const confirmFirmwareImageButton = connectedDeviceContainer.querySelector(".confirmFirmwareImage");
880
+ confirmFirmwareImageButton.addEventListener("click", () => {
881
+ device.confirmFirmwareImage();
882
+ });
883
+ const updateConfirmFirmwareImageButton = () => {
884
+ const enabled = device.firmwareStatus == "testing";
885
+ confirmFirmwareImageButton.disabled = !enabled;
886
+ };
887
+
888
+ /** @type {HTMLButtonElement} */
889
+ const eraseFirmwareImageButton = connectedDeviceContainer.querySelector(".eraseFirmwareImage");
890
+ eraseFirmwareImageButton.addEventListener("click", () => {
891
+ device.eraseFirmwareImage();
892
+ });
893
+ const updateEraseFirmwareImageButton = () => {
894
+ const enabled = device.firmwareStatus == "uploaded";
895
+ eraseFirmwareImageButton.disabled = !enabled;
896
+ };
897
+
898
+ const updateFirmwareUI = () => {
899
+ firmwareStatusSpan.innerText = device.firmwareStatus;
900
+
901
+ updateResetButton();
902
+ updateTestFirmwareImageButton();
903
+ updateConfirmFirmwareImageButton();
904
+ updateEraseFirmwareImageButton();
905
+ };
906
+ device.addEventListener("firmwareStatus", () => {
907
+ updateFirmwareUI();
908
+ });
909
+ updateFirmwareUI();
910
+
911
+ device.getFirmwareImages();
912
+
913
+ connectedDevicesContainer.appendChild(connectedDeviceContainer);
914
+ }
915
+ });
916
+
917
+ for (const id in connectedDeviceContainers) {
918
+ const connectedDevice = connectedDevices.find((connectedDevice) => connectedDevice.bluetoothId == id);
919
+ if (!connectedDevice) {
920
+ console.log("remove", id);
921
+ connectedDeviceContainers[id].remove();
922
+ delete connectedDeviceContainers[id];
923
+ }
924
+ }
925
+ });