brilliantsole 0.0.26 → 0.0.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/README.md +16 -10
  2. package/assets/3d/anchor.glb +0 -0
  3. package/assets/3d/coin.glb +0 -0
  4. package/assets/3d/glasses.glb +0 -0
  5. package/assets/3d/rightHand.glb +0 -0
  6. package/assets/audio/bounceMedium.wav +0 -0
  7. package/assets/audio/bounceStrong.wav +0 -0
  8. package/assets/audio/bounceWeak.wav +0 -0
  9. package/assets/audio/coin.wav +0 -0
  10. package/assets/audio/getUp.wav +0 -0
  11. package/assets/audio/grab.wav +0 -0
  12. package/assets/audio/kick.wav +0 -0
  13. package/assets/audio/platterFadeIn old.wav +0 -0
  14. package/assets/audio/platterFadeIn.wav +0 -0
  15. package/assets/audio/platterFadeOut.wav +0 -0
  16. package/assets/audio/punch.wav +0 -0
  17. package/assets/audio/punchSqueak.wav +0 -0
  18. package/assets/audio/purr.wav +0 -0
  19. package/assets/audio/purrFadeOut.wav +0 -0
  20. package/assets/audio/release.wav +0 -0
  21. package/assets/audio/splat.wav +0 -0
  22. package/assets/audio/stomp.wav +0 -0
  23. package/assets/images/ukaton-pressure-0.svg +9 -0
  24. package/assets/images/ukaton-pressure-1.svg +9 -0
  25. package/assets/images/ukaton-pressure-10.svg +9 -0
  26. package/assets/images/ukaton-pressure-11.svg +9 -0
  27. package/assets/images/ukaton-pressure-12.svg +9 -0
  28. package/assets/images/ukaton-pressure-13.svg +9 -0
  29. package/assets/images/ukaton-pressure-14.svg +9 -0
  30. package/assets/images/ukaton-pressure-15.svg +9 -0
  31. package/assets/images/ukaton-pressure-2.svg +9 -0
  32. package/assets/images/ukaton-pressure-3.svg +9 -0
  33. package/assets/images/ukaton-pressure-4.svg +9 -0
  34. package/assets/images/ukaton-pressure-5.svg +9 -0
  35. package/assets/images/ukaton-pressure-6.svg +9 -0
  36. package/assets/images/ukaton-pressure-7.svg +9 -0
  37. package/assets/images/ukaton-pressure-8.svg +9 -0
  38. package/assets/images/ukaton-pressure-9.svg +9 -0
  39. package/assets/images/ukaton-right-insole.svg +798 -0
  40. package/build/brilliantsole.cjs +2870 -882
  41. package/build/brilliantsole.cjs.map +1 -1
  42. package/build/brilliantsole.js +2477 -782
  43. package/build/brilliantsole.js.map +1 -1
  44. package/build/brilliantsole.ls.js +2260 -592
  45. package/build/brilliantsole.ls.js.map +1 -1
  46. package/build/brilliantsole.min.js +1 -1
  47. package/build/brilliantsole.min.js.map +1 -1
  48. package/build/brilliantsole.module.d.ts +302 -116
  49. package/build/brilliantsole.module.js +2468 -782
  50. package/build/brilliantsole.module.js.map +1 -1
  51. package/build/brilliantsole.module.min.d.ts +302 -116
  52. package/build/brilliantsole.module.min.js +1 -1
  53. package/build/brilliantsole.module.min.js.map +1 -1
  54. package/build/brilliantsole.node.module.d.ts +295 -113
  55. package/build/brilliantsole.node.module.js +2860 -882
  56. package/build/brilliantsole.node.module.js.map +1 -1
  57. package/build/dts/BS-output.d.ts +10 -0
  58. package/build/dts/BS.d.ts +21 -9
  59. package/build/dts/CameraManager.d.ts +72 -0
  60. package/build/dts/Device.d.ts +53 -16
  61. package/build/dts/DeviceInformationManager.d.ts +4 -4
  62. package/build/dts/DeviceManager.d.ts +3 -0
  63. package/build/dts/FileTransferManager.d.ts +18 -8
  64. package/build/dts/InformationManager.d.ts +8 -5
  65. package/build/dts/TfliteManager.d.ts +22 -2
  66. package/build/dts/WifiManager.d.ts +61 -0
  67. package/build/dts/connection/BaseConnectionManager.d.ts +37 -3
  68. package/build/dts/connection/ClientConnectionManager.d.ts +11 -2
  69. package/build/dts/connection/bluetooth/BluetoothConnectionManager.d.ts +1 -0
  70. package/build/dts/connection/bluetooth/NobleConnectionManager.d.ts +3 -1
  71. package/build/dts/connection/bluetooth/WebBluetoothConnectionManager.d.ts +2 -0
  72. package/build/dts/connection/bluetooth/bluetoothUUIDs.d.ts +2 -2
  73. package/build/dts/connection/udp/UDPConnectionManager.d.ts +28 -0
  74. package/build/dts/connection/webSocket/WebSocketConnectionManager.d.ts +25 -0
  75. package/build/dts/devicePair/DevicePair.d.ts +14 -10
  76. package/build/dts/devicePair/DevicePairPressureSensorDataManager.d.ts +8 -4
  77. package/build/dts/devicePair/DevicePairSensorDataManager.d.ts +2 -2
  78. package/build/dts/scanner/BaseScanner.d.ts +4 -1
  79. package/build/dts/scanner/NobleScanner.d.ts +2 -1
  80. package/build/dts/sensor/MotionSensorDataManager.d.ts +5 -2
  81. package/build/dts/sensor/SensorDataManager.d.ts +5 -4
  82. package/build/dts/server/BaseClient.d.ts +6 -3
  83. package/build/dts/server/ServerUtils.d.ts +1 -1
  84. package/build/dts/server/websocket/WebSocketUtils.d.ts +1 -1
  85. package/build/dts/utils/CenterOfPressureHelper.d.ts +2 -2
  86. package/build/dts/utils/Console.d.ts +2 -0
  87. package/build/dts/utils/MathUtils.d.ts +2 -0
  88. package/build/dts/utils/ThrottleUtils.d.ts +2 -0
  89. package/build/dts/vibration/VibrationManager.d.ts +19 -2
  90. package/build/index.d.ts +299 -113
  91. package/build/index.node.d.ts +292 -110
  92. package/examples/3d/scene.html +19 -5
  93. package/examples/3d/script.js +90 -17
  94. package/examples/3d-generic/index.html +144 -0
  95. package/examples/3d-generic/script.js +266 -0
  96. package/examples/balance/script.js +2 -1
  97. package/examples/basic/index.html +232 -18
  98. package/examples/basic/script.js +746 -106
  99. package/examples/bottango/index.html +11 -1
  100. package/examples/bottango/script.js +2 -2
  101. package/examples/center-of-pressure/index.html +114 -114
  102. package/examples/center-of-pressure/script.js +1 -1
  103. package/examples/device-pair/index.html +58 -58
  104. package/examples/device-pair/script.js +12 -8
  105. package/examples/edge-impulse/script.js +135 -44
  106. package/examples/edge-impulse-test/README.md +11 -0
  107. package/examples/edge-impulse-test/edge-impulse-standalone.js +7228 -0
  108. package/examples/edge-impulse-test/edge-impulse-standalone.wasm +0 -0
  109. package/examples/edge-impulse-test/index.html +75 -0
  110. package/examples/edge-impulse-test/run-impulse.js +135 -0
  111. package/examples/edge-impulse-test/script.js +200 -0
  112. package/examples/gloves/edge-impulse-standalone.js +7228 -0
  113. package/examples/gloves/edge-impulse-standalone.wasm +0 -0
  114. package/examples/gloves/index.html +119 -0
  115. package/examples/gloves/run-impulse.js +135 -0
  116. package/examples/gloves/scene.html +124 -0
  117. package/examples/gloves/script.js +931 -0
  118. package/examples/graph/index.html +11 -1
  119. package/examples/graph/script.js +94 -37
  120. package/examples/pressure/index.html +180 -12
  121. package/examples/pressure/script.js +144 -7
  122. package/examples/punch/index.html +135 -0
  123. package/examples/punch/punch.tflite +0 -0
  124. package/examples/punch/script.js +169 -0
  125. package/examples/recording/index.html +191 -183
  126. package/examples/server/index.html +109 -23
  127. package/examples/server/script.js +322 -111
  128. package/examples/ukaton-firmware-update/index.html +20 -0
  129. package/examples/ukaton-firmware-update/manifest.json +11 -0
  130. package/examples/ukaton-firmware-update/merged-firmware.bin +0 -0
  131. package/examples/utils/aframe/aframe-master.min.js +2 -0
  132. package/examples/utils/aframe/bs-vibration.js +150 -0
  133. package/examples/utils/aframe/force-pushable.js +80 -0
  134. package/examples/utils/aframe/grabbable-anchor.js +46 -0
  135. package/examples/utils/aframe/grabbable-listener.js +31 -0
  136. package/examples/utils/aframe/grabbable-physics-body.js +190 -0
  137. package/examples/utils/aframe/grow-shrink.js +25 -0
  138. package/examples/utils/aframe/hand-punch.js +119 -0
  139. package/examples/utils/aframe/my-obb-collider.js +293 -0
  140. package/examples/utils/aframe/occlude-hand-tracking-controls.js +47 -0
  141. package/examples/utils/aframe/occlude-mesh.js +42 -0
  142. package/examples/utils/aframe/palm-up-detector.js +47 -0
  143. package/examples/utils/aframe/shadow-material.js +20 -0
  144. package/examples/utils/aframe/soft-shadow-light.js +9 -0
  145. package/examples/webxr/script.js +3 -3
  146. package/examples/webxr-2/assets/3d/soccerBall.glb +0 -0
  147. package/examples/webxr-2/assets/audio/shellBounce.wav +0 -0
  148. package/examples/webxr-2/assets/audio/shellHit.wav +0 -0
  149. package/examples/webxr-2/assets/audio/shellKick.wav +0 -0
  150. package/examples/webxr-2/assets/audio/soccerBounce.wav +0 -0
  151. package/examples/webxr-2/assets/audio/soccerKick.mp3 +0 -0
  152. package/examples/webxr-2/assets/images/shellTexture.png +0 -0
  153. package/examples/webxr-2/components/bs-ankle.js +337 -0
  154. package/examples/webxr-2/components/coin.js +84 -0
  155. package/examples/webxr-2/components/custom-wrap.js +17 -0
  156. package/examples/webxr-2/components/goomba.js +3250 -0
  157. package/examples/webxr-2/components/init-shell-material.js +215 -0
  158. package/examples/webxr-2/components/platter.js +172 -0
  159. package/examples/webxr-2/components/shell.js +374 -0
  160. package/examples/webxr-2/components/soccer-ball.js +250 -0
  161. package/examples/webxr-2/components/squashed-goomba.js +249 -0
  162. package/examples/webxr-2/edge-impulse-standalone.js +7228 -0
  163. package/examples/webxr-2/edge-impulse-standalone.wasm +0 -0
  164. package/examples/webxr-2/index.html +996 -0
  165. package/examples/webxr-2/kick.tflite +0 -0
  166. package/examples/webxr-2/kick2.tflite +0 -0
  167. package/examples/webxr-2/run-impulse.js +135 -0
  168. package/examples/webxr-2/script.js +384 -0
  169. package/package.json +2 -1
  170. package/src/.prettierrc +4 -0
  171. package/src/BS.ts +66 -9
  172. package/src/CameraManager.ts +499 -0
  173. package/src/Device.ts +620 -92
  174. package/src/DeviceInformationManager.ts +22 -11
  175. package/src/DeviceManager.ts +94 -25
  176. package/src/FileTransferManager.ts +146 -21
  177. package/src/FirmwareManager.ts +1 -1
  178. package/src/InformationManager.ts +62 -20
  179. package/src/TfliteManager.ts +172 -26
  180. package/src/WifiManager.ts +323 -0
  181. package/src/connection/BaseConnectionManager.ts +145 -30
  182. package/src/connection/ClientConnectionManager.ts +47 -11
  183. package/src/connection/bluetooth/BluetoothConnectionManager.ts +14 -3
  184. package/src/connection/bluetooth/NobleConnectionManager.ts +155 -42
  185. package/src/connection/bluetooth/WebBluetoothConnectionManager.ts +104 -35
  186. package/src/connection/bluetooth/bluetoothUUIDs.ts +40 -13
  187. package/src/connection/udp/UDPConnectionManager.ts +356 -0
  188. package/src/connection/websocket/WebSocketConnectionManager.ts +282 -0
  189. package/src/devicePair/DevicePair.ts +145 -49
  190. package/src/devicePair/DevicePairPressureSensorDataManager.ts +72 -24
  191. package/src/devicePair/DevicePairSensorDataManager.ts +5 -5
  192. package/src/scanner/BaseScanner.ts +49 -11
  193. package/src/scanner/NobleScanner.ts +81 -17
  194. package/src/sensor/BarometerSensorDataManager.ts +1 -1
  195. package/src/sensor/MotionSensorDataManager.ts +22 -7
  196. package/src/sensor/PressureSensorDataManager.ts +47 -13
  197. package/src/sensor/SensorConfigurationManager.ts +75 -24
  198. package/src/sensor/SensorDataManager.ts +107 -26
  199. package/src/server/BaseClient.ts +192 -37
  200. package/src/server/BaseServer.ts +201 -43
  201. package/src/server/ServerUtils.ts +39 -9
  202. package/src/server/udp/UDPServer.ts +74 -23
  203. package/src/server/udp/UDPUtils.ts +9 -2
  204. package/src/server/websocket/WebSocketClient.ts +30 -9
  205. package/src/server/websocket/WebSocketServer.ts +1 -1
  206. package/src/server/websocket/WebSocketUtils.ts +4 -2
  207. package/src/utils/CenterOfPressureHelper.ts +5 -5
  208. package/src/utils/Console.ts +62 -9
  209. package/src/utils/MathUtils.ts +31 -1
  210. package/src/utils/ParseUtils.ts +25 -6
  211. package/src/utils/ThrottleUtils.ts +62 -0
  212. package/src/utils/Timer.ts +1 -1
  213. package/src/utils/checksum.ts +1 -1
  214. package/src/utils/mcumgr.js +1 -1
  215. package/src/vibration/VibrationManager.ts +166 -40
@@ -1,6 +1,14 @@
1
1
  import { createConsole } from "../../utils/Console.ts";
2
- import { isInNode, isInBrowser, isInBluefy, isInWebBLE } from "../../utils/environment.ts";
3
- import { addEventListeners, removeEventListeners } from "../../utils/EventUtils.ts";
2
+ import {
3
+ isInNode,
4
+ isInBrowser,
5
+ isInBluefy,
6
+ isInWebBLE,
7
+ } from "../../utils/environment.ts";
8
+ import {
9
+ addEventListeners,
10
+ removeEventListeners,
11
+ } from "../../utils/EventUtils.ts";
4
12
  import {
5
13
  serviceUUIDs,
6
14
  optionalServiceUUIDs,
@@ -9,10 +17,13 @@ import {
9
17
  getCharacteristicProperties,
10
18
  } from "./bluetoothUUIDs.ts";
11
19
  import BluetoothConnectionManager from "./BluetoothConnectionManager.ts";
12
- import { BluetoothCharacteristicName, BluetoothServiceName } from "./bluetoothUUIDs.ts";
20
+ import {
21
+ BluetoothCharacteristicName,
22
+ BluetoothServiceName,
23
+ } from "./bluetoothUUIDs.ts";
13
24
  import { ConnectionType } from "../BaseConnectionManager.ts";
14
25
 
15
- const _console = createConsole("WebBluetoothConnectionManager", { log: true });
26
+ const _console = createConsole("WebBluetoothConnectionManager", { log: false });
16
27
 
17
28
  type WebBluetoothInterface = webbluetooth.Bluetooth | Bluetooth;
18
29
 
@@ -42,12 +53,19 @@ class WebBluetoothConnectionManager extends BluetoothConnectionManager {
42
53
  return this.device!.id;
43
54
  }
44
55
 
45
- #boundBluetoothCharacteristicEventListeners: { [eventType: string]: EventListener } = {
56
+ get canUpdateFirmware() {
57
+ return this.#characteristics.has("smp");
58
+ }
59
+
60
+ #boundBluetoothCharacteristicEventListeners: {
61
+ [eventType: string]: EventListener;
62
+ } = {
46
63
  characteristicvaluechanged: this.#onCharacteristicvaluechanged.bind(this),
47
64
  };
48
- #boundBluetoothDeviceEventListeners: { [eventType: string]: EventListener } = {
49
- gattserverdisconnected: this.#onGattserverdisconnected.bind(this),
50
- };
65
+ #boundBluetoothDeviceEventListeners: { [eventType: string]: EventListener } =
66
+ {
67
+ gattserverdisconnected: this.#onGattserverdisconnected.bind(this),
68
+ };
51
69
 
52
70
  static get isSupported() {
53
71
  return Boolean(bluetooth);
@@ -56,7 +74,7 @@ class WebBluetoothConnectionManager extends BluetoothConnectionManager {
56
74
  return "webBluetooth";
57
75
  }
58
76
 
59
- #device!: BluetoothDevice | undefined;
77
+ #device?: BluetoothDevice;
60
78
  get device() {
61
79
  return this.#device;
62
80
  }
@@ -66,7 +84,10 @@ class WebBluetoothConnectionManager extends BluetoothConnectionManager {
66
84
  return;
67
85
  }
68
86
  if (this.#device) {
69
- removeEventListeners(this.#device, this.#boundBluetoothDeviceEventListeners);
87
+ removeEventListeners(
88
+ this.#device,
89
+ this.#boundBluetoothDeviceEventListeners
90
+ );
70
91
  }
71
92
  if (newDevice) {
72
93
  addEventListeners(newDevice, this.#boundBluetoothDeviceEventListeners);
@@ -82,7 +103,8 @@ class WebBluetoothConnectionManager extends BluetoothConnectionManager {
82
103
  }
83
104
 
84
105
  #services: Map<BluetoothServiceName, BluetoothService> = new Map();
85
- #characteristics: Map<BluetoothCharacteristicName, BluetoothCharacteristic> = new Map();
106
+ #characteristics: Map<BluetoothCharacteristicName, BluetoothCharacteristic> =
107
+ new Map();
86
108
 
87
109
  async connect() {
88
110
  await super.connect();
@@ -125,7 +147,10 @@ class WebBluetoothConnectionManager extends BluetoothConnectionManager {
125
147
  const service = services[serviceIndex] as BluetoothService;
126
148
  _console.log({ service });
127
149
  const serviceName = getServiceNameFromUUID(service.uuid)!;
128
- _console.assertWithError(serviceName, `no name found for service uuid "${service.uuid}"`);
150
+ _console.assertWithError(
151
+ serviceName,
152
+ `no name found for service uuid "${service.uuid}"`
153
+ );
129
154
  _console.log(`got "${serviceName}" service`);
130
155
  service.name = serviceName;
131
156
  this.#services.set(serviceName, service);
@@ -133,20 +158,33 @@ class WebBluetoothConnectionManager extends BluetoothConnectionManager {
133
158
  const characteristics = await service.getCharacteristics();
134
159
  _console.log(`got characteristics for "${serviceName}" service`);
135
160
  for (const characteristicIndex in characteristics) {
136
- const characteristic = characteristics[characteristicIndex] as BluetoothCharacteristic;
161
+ const characteristic = characteristics[
162
+ characteristicIndex
163
+ ] as BluetoothCharacteristic;
137
164
  _console.log({ characteristic });
138
- const characteristicName = getCharacteristicNameFromUUID(characteristic.uuid)!;
165
+ const characteristicName = getCharacteristicNameFromUUID(
166
+ characteristic.uuid
167
+ )!;
139
168
  _console.assertWithError(
140
169
  Boolean(characteristicName),
141
170
  `no name found for characteristic uuid "${characteristic.uuid}" in "${serviceName}" service`
142
171
  );
143
- _console.log(`got "${characteristicName}" characteristic in "${serviceName}" service`);
172
+ _console.log(
173
+ `got "${characteristicName}" characteristic in "${serviceName}" service`
174
+ );
144
175
  characteristic.name = characteristicName;
145
176
  this.#characteristics.set(characteristicName, characteristic);
146
- addEventListeners(characteristic, this.#boundBluetoothCharacteristicEventListeners);
147
- const characteristicProperties = characteristic.properties || getCharacteristicProperties(characteristicName);
177
+ addEventListeners(
178
+ characteristic,
179
+ this.#boundBluetoothCharacteristicEventListeners
180
+ );
181
+ const characteristicProperties =
182
+ characteristic.properties ||
183
+ getCharacteristicProperties(characteristicName);
148
184
  if (characteristicProperties.notify) {
149
- _console.log(`starting notifications for "${characteristicName}" characteristic`);
185
+ _console.log(
186
+ `starting notifications for "${characteristicName}" characteristic`
187
+ );
150
188
  await characteristic.startNotifications();
151
189
  }
152
190
  if (characteristicProperties.read) {
@@ -161,18 +199,30 @@ class WebBluetoothConnectionManager extends BluetoothConnectionManager {
161
199
  }
162
200
  async #removeEventListeners() {
163
201
  if (this.device) {
164
- removeEventListeners(this.device, this.#boundBluetoothDeviceEventListeners);
202
+ removeEventListeners(
203
+ this.device,
204
+ this.#boundBluetoothDeviceEventListeners
205
+ );
165
206
  }
166
207
 
167
- const promises = Array.from(this.#characteristics.keys()).map((characteristicName) => {
168
- const characteristic = this.#characteristics.get(characteristicName)!;
169
- removeEventListeners(characteristic, this.#boundBluetoothCharacteristicEventListeners);
170
- const characteristicProperties = characteristic.properties || getCharacteristicProperties(characteristicName);
171
- if (characteristicProperties.notify) {
172
- _console.log(`stopping notifications for "${characteristicName}" characteristic`);
173
- return characteristic.stopNotifications();
208
+ const promises = Array.from(this.#characteristics.keys()).map(
209
+ (characteristicName) => {
210
+ const characteristic = this.#characteristics.get(characteristicName)!;
211
+ removeEventListeners(
212
+ characteristic,
213
+ this.#boundBluetoothCharacteristicEventListeners
214
+ );
215
+ const characteristicProperties =
216
+ characteristic.properties ||
217
+ getCharacteristicProperties(characteristicName);
218
+ if (characteristicProperties.notify) {
219
+ _console.log(
220
+ `stopping notifications for "${characteristicName}" characteristic`
221
+ );
222
+ return characteristic.stopNotifications();
223
+ }
174
224
  }
175
- });
225
+ );
176
226
 
177
227
  return Promise.allSettled(promises);
178
228
  }
@@ -199,10 +249,18 @@ class WebBluetoothConnectionManager extends BluetoothConnectionManager {
199
249
  `no name found for characteristic with uuid "${characteristic.uuid}"`
200
250
  );
201
251
 
202
- _console.log(`oncharacteristicvaluechanged for "${characteristicName}" characteristic`);
252
+ _console.log(
253
+ `oncharacteristicvaluechanged for "${characteristicName}" characteristic`
254
+ );
203
255
  const dataView = characteristic.value!;
204
- _console.assertWithError(dataView, `no data found for "${characteristicName}" characteristic`);
205
- _console.log(`data for "${characteristicName}" characteristic`, Array.from(new Uint8Array(dataView.buffer)));
256
+ _console.assertWithError(
257
+ dataView,
258
+ `no data found for "${characteristicName}" characteristic`
259
+ );
260
+ _console.log(
261
+ `data for "${characteristicName}" characteristic`,
262
+ Array.from(new Uint8Array(dataView.buffer))
263
+ );
206
264
 
207
265
  try {
208
266
  this.onCharacteristicValueChanged(characteristicName, dataView);
@@ -211,13 +269,21 @@ class WebBluetoothConnectionManager extends BluetoothConnectionManager {
211
269
  }
212
270
  }
213
271
 
214
- async writeCharacteristic(characteristicName: BluetoothCharacteristicName, data: ArrayBuffer) {
272
+ async writeCharacteristic(
273
+ characteristicName: BluetoothCharacteristicName,
274
+ data: ArrayBuffer
275
+ ) {
215
276
  super.writeCharacteristic(characteristicName, data);
216
277
 
217
278
  const characteristic = this.#characteristics.get(characteristicName)!;
218
- _console.assertWithError(characteristic, `${characteristicName} characteristic not found`);
279
+ _console.assertWithError(
280
+ characteristic,
281
+ `${characteristicName} characteristic not found`
282
+ );
219
283
  _console.log("writing characteristic", characteristic, data);
220
- const characteristicProperties = characteristic.properties || getCharacteristicProperties(characteristicName);
284
+ const characteristicProperties =
285
+ characteristic.properties ||
286
+ getCharacteristicProperties(characteristicName);
221
287
  if (characteristicProperties.writeWithoutResponse) {
222
288
  _console.log("writing without response");
223
289
  await characteristic.writeValueWithoutResponse(data);
@@ -246,8 +312,6 @@ class WebBluetoothConnectionManager extends BluetoothConnectionManager {
246
312
  }
247
313
  async reconnect() {
248
314
  await super.reconnect();
249
- _console.log("attempting to reconnect...");
250
- this.status = "connecting";
251
315
  try {
252
316
  await this.server!.connect();
253
317
  } catch (error) {
@@ -264,6 +328,11 @@ class WebBluetoothConnectionManager extends BluetoothConnectionManager {
264
328
  this.status = "notConnected";
265
329
  }
266
330
  }
331
+
332
+ remove() {
333
+ super.remove();
334
+ this.device = undefined;
335
+ }
267
336
  }
268
337
 
269
338
  export default WebBluetoothConnectionManager;
@@ -15,11 +15,16 @@ if (isInBrowser) {
15
15
 
16
16
  function generateBluetoothUUID(value: string): BluetoothServiceUUID {
17
17
  _console.assertTypeWithError(value, "string");
18
- _console.assertWithError(value.length == 4, "value must be 4 characters long");
19
- return `ea6da725-${value}-4f9b-893d-c3913e33b39f`;
18
+ _console.assertWithError(
19
+ value.length == 4,
20
+ "value must be 4 characters long"
21
+ );
22
+ return `ea6d${value}-a725-4f9b-893d-c3913e33b39f`;
20
23
  }
21
24
 
22
- function stringToCharacteristicUUID(identifier: string): BluetoothCharacteristicUUID {
25
+ function stringToCharacteristicUUID(
26
+ identifier: string
27
+ ): BluetoothCharacteristicUUID {
23
28
  return BluetoothUUID?.getCharacteristic?.(identifier);
24
29
  }
25
30
 
@@ -27,19 +32,32 @@ function stringToServiceUUID(identifier: string): BluetoothServiceUUID {
27
32
  return BluetoothUUID?.getService?.(identifier);
28
33
  }
29
34
 
30
- export type BluetoothServiceName = "deviceInformation" | "battery" | "main" | "smp";
31
- import { DeviceInformationMessageType } from "../../DeviceInformationManager.ts";
32
- export type BluetoothCharacteristicName = DeviceInformationMessageType | "batteryLevel" | "rx" | "tx" | "smp";
35
+ export type BluetoothServiceName =
36
+ | "deviceInformation"
37
+ | "battery"
38
+ | "main"
39
+ | "smp";
40
+ import { DeviceInformationType } from "../../DeviceInformationManager.ts";
41
+ export type BluetoothCharacteristicName =
42
+ | DeviceInformationType
43
+ | "batteryLevel"
44
+ | "rx"
45
+ | "tx"
46
+ | "smp";
33
47
 
34
48
  interface BluetoothCharacteristicInformation {
35
49
  uuid: BluetoothCharacteristicUUID;
36
50
  }
37
51
  interface BluetoothServiceInformation {
38
52
  uuid: BluetoothServiceUUID;
39
- characteristics: { [characteristicName in BluetoothCharacteristicName]?: BluetoothCharacteristicInformation };
53
+ characteristics: {
54
+ [characteristicName in BluetoothCharacteristicName]?: BluetoothCharacteristicInformation;
55
+ };
40
56
  }
41
57
  interface BluetoothServicesInformation {
42
- services: { [serviceName in BluetoothServiceName]: BluetoothServiceInformation };
58
+ services: {
59
+ [serviceName in BluetoothServiceName]: BluetoothServiceInformation;
60
+ };
43
61
  }
44
62
  const bluetoothUUIDs: BluetoothServicesInformation = Object.freeze({
45
63
  services: {
@@ -101,9 +119,13 @@ export const optionalServiceUUIDs = [
101
119
  ];
102
120
  export const allServiceUUIDs = [...serviceUUIDs, ...optionalServiceUUIDs];
103
121
 
104
- export function getServiceNameFromUUID(serviceUUID: BluetoothServiceUUID): BluetoothServiceName | undefined {
122
+ export function getServiceNameFromUUID(
123
+ serviceUUID: BluetoothServiceUUID
124
+ ): BluetoothServiceName | undefined {
105
125
  serviceUUID = serviceUUID.toString().toLowerCase();
106
- const serviceNames = Object.keys(bluetoothUUIDs.services) as BluetoothServiceName[];
126
+ const serviceNames = Object.keys(
127
+ bluetoothUUIDs.services
128
+ ) as BluetoothServiceName[];
107
129
  return serviceNames.find((serviceName) => {
108
130
  const serviceInfo = bluetoothUUIDs.services[serviceName];
109
131
  let serviceInfoUUID = serviceInfo.uuid.toString();
@@ -127,7 +149,9 @@ Object.values(bluetoothUUIDs.services).forEach((serviceInfo) => {
127
149
  if (!serviceInfo.characteristics) {
128
150
  return;
129
151
  }
130
- const characteristicNames = Object.keys(serviceInfo.characteristics) as BluetoothCharacteristicName[];
152
+ const characteristicNames = Object.keys(
153
+ serviceInfo.characteristics
154
+ ) as BluetoothCharacteristicName[];
131
155
  characteristicNames.forEach((characteristicName) => {
132
156
  const characteristicInfo = serviceInfo.characteristics[characteristicName]!;
133
157
  if (serviceUUIDs.includes(serviceInfo.uuid)) {
@@ -148,9 +172,12 @@ export function getCharacteristicNameFromUUID(
148
172
  characteristicUUID = characteristicUUID.toString().toLowerCase();
149
173
  var characteristicName: BluetoothCharacteristicName | undefined;
150
174
  Object.values(bluetoothUUIDs.services).some((serviceInfo) => {
151
- const characteristicNames = Object.keys(serviceInfo.characteristics) as BluetoothCharacteristicName[];
175
+ const characteristicNames = Object.keys(
176
+ serviceInfo.characteristics
177
+ ) as BluetoothCharacteristicName[];
152
178
  characteristicName = characteristicNames.find((_characteristicName) => {
153
- const characteristicInfo = serviceInfo.characteristics[_characteristicName]!;
179
+ const characteristicInfo =
180
+ serviceInfo.characteristics[_characteristicName]!;
154
181
  let characteristicInfoUUID = characteristicInfo.uuid.toString();
155
182
  if (characteristicUUID.length == 4) {
156
183
  characteristicInfoUUID = characteristicInfoUUID.slice(4, 8);
@@ -0,0 +1,356 @@
1
+ import { DeviceInformationTypes } from "../../DeviceInformationManager.ts";
2
+ import {
3
+ createMessage,
4
+ Message,
5
+ MessageLike,
6
+ } from "../../server/ServerUtils.ts";
7
+ import { createConsole } from "../../utils/Console.ts";
8
+ import { isInNode } from "../../utils/environment.ts";
9
+ import {
10
+ addEventListeners,
11
+ removeEventListeners,
12
+ } from "../../utils/EventUtils.ts";
13
+ import { parseMessage } from "../../utils/ParseUtils.ts";
14
+ import Timer from "../../utils/Timer.ts";
15
+ import BaseConnectionManager, {
16
+ ConnectionType,
17
+ } from "../BaseConnectionManager.ts";
18
+
19
+ import * as dgram from "dgram";
20
+
21
+ const _console = createConsole("UDPConnectionManager", { log: false });
22
+
23
+ export const UDPSendPort = 3000;
24
+
25
+ export const UDPPingInterval = 2_000;
26
+
27
+ const SocketMessageTypes = [
28
+ "ping",
29
+ "pong",
30
+ "setRemoteReceivePort",
31
+ "batteryLevel",
32
+ "deviceInformation",
33
+ "message",
34
+ ] as const;
35
+ type SocketMessageType = (typeof SocketMessageTypes)[number];
36
+
37
+ type SocketMessage = SocketMessageType | Message<SocketMessageType>;
38
+ function createSocketMessage(...messages: SocketMessage[]) {
39
+ _console.log("createSocketMessage", ...messages);
40
+ return createMessage(SocketMessageTypes, ...messages);
41
+ }
42
+
43
+ const SocketDeviceInformationMessageTypes: SocketMessageType[] = [
44
+ "deviceInformation",
45
+ "batteryLevel",
46
+ ];
47
+
48
+ class UDPConnectionManager extends BaseConnectionManager {
49
+ #bluetoothId?: string;
50
+ get bluetoothId() {
51
+ return this.#bluetoothId ?? "";
52
+ }
53
+
54
+ defaultMtu = 2 ** 10;
55
+
56
+ constructor(ipAddress: string, bluetoothId?: string, receivePort?: number) {
57
+ super();
58
+ this.ipAddress = ipAddress;
59
+ this.mtu = this.defaultMtu;
60
+ this.#bluetoothId = bluetoothId;
61
+ if (receivePort) {
62
+ this.receivePort = receivePort;
63
+ }
64
+ }
65
+
66
+ get isAvailable() {
67
+ return true;
68
+ }
69
+ static get isSupported() {
70
+ return isInNode;
71
+ }
72
+ static get type(): ConnectionType {
73
+ return "udp";
74
+ }
75
+
76
+ // IP ADDRESS
77
+ #ipAddress!: string;
78
+ get ipAddress() {
79
+ return this.#ipAddress;
80
+ }
81
+ set ipAddress(newIpAddress) {
82
+ this.assertIsNotConnected();
83
+ if (this.#ipAddress == newIpAddress) {
84
+ _console.log(`redundnant ipAddress assignment "${newIpAddress}"`);
85
+ return;
86
+ }
87
+ this.#ipAddress = newIpAddress;
88
+ _console.log(`updated ipAddress to "${this.ipAddress}"`);
89
+ }
90
+
91
+ // RECEIVE PORT
92
+ #receivePort?: number;
93
+ get receivePort() {
94
+ return this.#receivePort;
95
+ }
96
+ set receivePort(newReceivePort) {
97
+ this.assertIsNotConnected();
98
+ if (this.#receivePort == newReceivePort) {
99
+ _console.log(`redundnant receivePort assignment ${newReceivePort}`);
100
+ return;
101
+ }
102
+ this.#receivePort = newReceivePort;
103
+ _console.log(`updated receivePort to ${this.#receivePort}`);
104
+ if (this.#receivePort) {
105
+ this.#setRemoteReceivePortDataView.setUint16(0, this.#receivePort, true);
106
+ }
107
+ }
108
+
109
+ // SET REMOTE RECEIVE PORT
110
+ #didSetRemoteReceivePort = false;
111
+ #setRemoteReceivePortDataView = new DataView(new ArrayBuffer(2));
112
+ #parseReceivePort(dataView: DataView) {
113
+ const parsedReceivePort = dataView.getUint16(0, true);
114
+ if (parsedReceivePort != this.receivePort) {
115
+ _console.error(
116
+ `incorrect receivePort (expected ${this.receivePort}, got ${parsedReceivePort})`
117
+ );
118
+ return;
119
+ }
120
+ this.#didSetRemoteReceivePort = true;
121
+ }
122
+
123
+ // SOCKET
124
+ #socket?: dgram.Socket;
125
+ get socket() {
126
+ return this.#socket;
127
+ }
128
+ set socket(newSocket) {
129
+ if (this.#socket == newSocket) {
130
+ _console.log("redundant socket assignment");
131
+ return;
132
+ }
133
+
134
+ _console.log("assigning socket", newSocket);
135
+
136
+ if (this.#socket) {
137
+ _console.log("removing existing socket...");
138
+ removeEventListeners(this.#socket, this.#boundSocketEventListeners);
139
+ try {
140
+ this.#socket.close();
141
+ } catch (error) {
142
+ _console.error(error);
143
+ }
144
+ }
145
+
146
+ if (newSocket) {
147
+ addEventListeners(newSocket, this.#boundSocketEventListeners);
148
+ }
149
+ this.#socket = newSocket;
150
+
151
+ _console.log("assigned socket");
152
+ }
153
+
154
+ // SOCKET MESSAGING
155
+ #sendMessage(message: MessageLike) {
156
+ // this.assertIsConnected();
157
+ _console.log("sending socket message", message);
158
+ const dataView = Buffer.from(message);
159
+ this.#socket!.send(dataView);
160
+ this.#pingTimer.restart();
161
+ }
162
+
163
+ #sendSocketMessage(...messages: SocketMessage[]) {
164
+ this.#sendMessage(createSocketMessage(...messages));
165
+ }
166
+
167
+ // BASE CONNECTION MANAGER
168
+ async sendSmpMessage(data: ArrayBuffer) {
169
+ super.sendSmpMessage(data);
170
+ _console.error("smp not supported on udp");
171
+ }
172
+
173
+ async sendTxData(data: ArrayBuffer) {
174
+ super.sendTxData(data);
175
+ if (data.byteLength == 0) {
176
+ return;
177
+ }
178
+ this.#sendSocketMessage({ type: "message", data });
179
+ }
180
+
181
+ // SOCKET EVENTS
182
+ #boundSocketEventListeners: { [eventType: string]: Function } = {
183
+ close: this.#onSocketClose.bind(this),
184
+ connect: this.#onSocketConnect.bind(this),
185
+ error: this.#onSocketError.bind(this),
186
+ listening: this.#onSocketListening.bind(this),
187
+ message: this.#onSocketMessage.bind(this),
188
+ };
189
+
190
+ #onSocketClose() {
191
+ _console.log("socket.close");
192
+ this.status = "notConnected";
193
+ this.clear();
194
+ }
195
+ #onSocketConnect() {
196
+ _console.log("socket.connect");
197
+ this.#pingTimer.start(true);
198
+ }
199
+ #onSocketError(error: Error) {
200
+ _console.error("socket.error", error);
201
+ }
202
+ #onSocketListening() {
203
+ const address = this.socket!.address();
204
+ _console.log(`socket.listening on ${address.address}:${address.port}`);
205
+ this.receivePort = address.port;
206
+ this.socket!.connect(UDPSendPort, this.ipAddress);
207
+ }
208
+ #onSocketMessage(message: Buffer, remoteInfo: dgram.RemoteInfo) {
209
+ this.#pongTimeoutTimer.stop();
210
+ _console.log("socket.message", message.byteLength, remoteInfo);
211
+ const arrayBuffer = message.buffer.slice(
212
+ message.byteOffset,
213
+ message.byteOffset + message.byteLength
214
+ );
215
+ const dataView = new DataView(arrayBuffer);
216
+ this.#parseSocketMessage(dataView);
217
+
218
+ if (this.status == "connecting" && this.#didSetRemoteReceivePort) {
219
+ this.status = "connected";
220
+ this.#requestDeviceInformation();
221
+ }
222
+ }
223
+
224
+ #setupSocket() {
225
+ this.#didSetRemoteReceivePort = false;
226
+ this.socket = dgram.createSocket({
227
+ type: "udp4",
228
+ });
229
+ try {
230
+ if (this.receivePort) {
231
+ this.socket.bind(this.receivePort);
232
+ } else {
233
+ this.socket.bind();
234
+ }
235
+ } catch (error) {
236
+ _console.error(error);
237
+ this.disconnect();
238
+ }
239
+ }
240
+
241
+ // CONNECTION
242
+ async connect() {
243
+ await super.connect();
244
+ this.#setupSocket();
245
+ }
246
+ async disconnect() {
247
+ await super.disconnect();
248
+ _console.log("closing socket");
249
+ try {
250
+ this.#socket?.close();
251
+ } catch (error) {
252
+ _console.error(error);
253
+ }
254
+ this.#pingTimer.stop();
255
+ }
256
+
257
+ get canReconnect() {
258
+ return Boolean(this.socket);
259
+ }
260
+ async reconnect() {
261
+ await super.reconnect();
262
+ this.#setupSocket();
263
+ }
264
+
265
+ // PARSING
266
+ #parseSocketMessage(dataView: DataView) {
267
+ parseMessage(
268
+ dataView,
269
+ SocketMessageTypes,
270
+ this.#onMessage.bind(this),
271
+ null,
272
+ true
273
+ );
274
+ }
275
+
276
+ #onMessage(messageType: SocketMessageType, dataView: DataView) {
277
+ _console.log(
278
+ `received "${messageType}" message (${dataView.byteLength} bytes)`
279
+ );
280
+ switch (messageType) {
281
+ case "ping":
282
+ this.#pong();
283
+ break;
284
+ case "pong":
285
+ break;
286
+ case "setRemoteReceivePort":
287
+ this.#parseReceivePort(dataView);
288
+ break;
289
+ case "batteryLevel":
290
+ this.onMessageReceived?.("batteryLevel", dataView);
291
+ break;
292
+ case "deviceInformation":
293
+ parseMessage(
294
+ dataView,
295
+ DeviceInformationTypes,
296
+ (deviceInformationType, dataView) => {
297
+ this.onMessageReceived!(deviceInformationType, dataView);
298
+ }
299
+ );
300
+ break;
301
+ case "message":
302
+ this.parseRxMessage(dataView);
303
+ break;
304
+ default:
305
+ _console.error(`uncaught messageType "${messageType}"`);
306
+ break;
307
+ }
308
+ }
309
+
310
+ // PING
311
+ #pingTimer = new Timer(this.#ping.bind(this), UDPPingInterval);
312
+ #ping() {
313
+ _console.log("pinging");
314
+ if (this.#didSetRemoteReceivePort || !this.#receivePort) {
315
+ this.#sendSocketMessage("ping");
316
+ } else {
317
+ this.#sendSocketMessage({
318
+ type: "setRemoteReceivePort",
319
+ data: this.#setRemoteReceivePortDataView,
320
+ });
321
+ }
322
+ if (this.isConnected) {
323
+ this.#pongTimeoutTimer.start();
324
+ }
325
+ }
326
+ #pong() {
327
+ _console.log("ponging");
328
+ this.#sendSocketMessage("pong");
329
+ }
330
+
331
+ #pongTimeout() {
332
+ this.#pongTimeoutTimer.stop();
333
+ _console.log("pong timeout");
334
+ this.disconnect();
335
+ }
336
+ #pongTimeoutTimer = new Timer(() => this.#pongTimeout(), 1_000);
337
+
338
+ // DEVICE INFORMATION
339
+ #requestDeviceInformation() {
340
+ this.#sendSocketMessage(...SocketDeviceInformationMessageTypes);
341
+ }
342
+
343
+ clear() {
344
+ super.clear();
345
+ this.#didSetRemoteReceivePort = false;
346
+ this.#pingTimer.stop();
347
+ this.#pongTimeoutTimer.stop();
348
+ }
349
+
350
+ remove() {
351
+ super.remove();
352
+ this.socket = undefined;
353
+ }
354
+ }
355
+
356
+ export default UDPConnectionManager;