brilliantsole 0.0.27 → 0.0.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (202) hide show
  1. package/assets/3d/anchor.glb +0 -0
  2. package/assets/3d/coin.glb +0 -0
  3. package/assets/3d/glasses.glb +0 -0
  4. package/assets/audio/bounceMedium.wav +0 -0
  5. package/assets/audio/bounceStrong.wav +0 -0
  6. package/assets/audio/bounceWeak.wav +0 -0
  7. package/assets/audio/coin.wav +0 -0
  8. package/assets/audio/getUp.wav +0 -0
  9. package/assets/audio/grab.wav +0 -0
  10. package/assets/audio/kick.wav +0 -0
  11. package/assets/audio/platterFadeIn old.wav +0 -0
  12. package/assets/audio/platterFadeIn.wav +0 -0
  13. package/assets/audio/platterFadeOut.wav +0 -0
  14. package/assets/audio/punch.wav +0 -0
  15. package/assets/audio/punchSqueak.wav +0 -0
  16. package/assets/audio/purr.wav +0 -0
  17. package/assets/audio/purrFadeOut.wav +0 -0
  18. package/assets/audio/release.wav +0 -0
  19. package/assets/audio/splat.wav +0 -0
  20. package/assets/audio/stomp.wav +0 -0
  21. package/build/brilliantsole.cjs +3091 -741
  22. package/build/brilliantsole.cjs.map +1 -1
  23. package/build/brilliantsole.js +2759 -709
  24. package/build/brilliantsole.js.map +1 -1
  25. package/build/brilliantsole.ls.js +2602 -543
  26. package/build/brilliantsole.ls.js.map +1 -1
  27. package/build/brilliantsole.min.js +1 -1
  28. package/build/brilliantsole.min.js.map +1 -1
  29. package/build/brilliantsole.module.d.ts +295 -65
  30. package/build/brilliantsole.module.js +2749 -710
  31. package/build/brilliantsole.module.js.map +1 -1
  32. package/build/brilliantsole.module.min.d.ts +295 -65
  33. package/build/brilliantsole.module.min.js +1 -1
  34. package/build/brilliantsole.module.min.js.map +1 -1
  35. package/build/brilliantsole.node.module.d.ts +289 -62
  36. package/build/brilliantsole.node.module.js +3080 -742
  37. package/build/brilliantsole.node.module.js.map +1 -1
  38. package/build/dts/BS-output.d.ts +10 -0
  39. package/build/dts/BS.d.ts +21 -8
  40. package/build/dts/CameraManager.d.ts +72 -0
  41. package/build/dts/Device.d.ts +64 -13
  42. package/build/dts/DeviceInformationManager.d.ts +4 -4
  43. package/build/dts/DeviceManager.d.ts +2 -0
  44. package/build/dts/FileTransferManager.d.ts +18 -8
  45. package/build/dts/InformationManager.d.ts +2 -0
  46. package/build/dts/MicrophoneManager.d.ts +88 -0
  47. package/build/dts/TfliteManager.d.ts +22 -2
  48. package/build/dts/WifiManager.d.ts +61 -0
  49. package/build/dts/connection/BaseConnectionManager.d.ts +35 -3
  50. package/build/dts/connection/ClientConnectionManager.d.ts +7 -2
  51. package/build/dts/connection/bluetooth/NobleConnectionManager.d.ts +2 -1
  52. package/build/dts/connection/bluetooth/WebBluetoothConnectionManager.d.ts +1 -0
  53. package/build/dts/connection/bluetooth/bluetoothUUIDs.d.ts +2 -2
  54. package/build/dts/connection/udp/UDPConnectionManager.d.ts +28 -0
  55. package/build/dts/connection/webSocket/WebSocketConnectionManager.d.ts +25 -0
  56. package/build/dts/devicePair/DevicePair.d.ts +5 -5
  57. package/build/dts/scanner/BaseScanner.d.ts +4 -1
  58. package/build/dts/scanner/NobleScanner.d.ts +2 -1
  59. package/build/dts/sensor/MotionSensorDataManager.d.ts +5 -2
  60. package/build/dts/sensor/SensorDataManager.d.ts +5 -4
  61. package/build/dts/server/BaseClient.d.ts +5 -3
  62. package/build/dts/server/ServerUtils.d.ts +1 -1
  63. package/build/dts/server/websocket/WebSocketUtils.d.ts +1 -1
  64. package/build/dts/utils/AudioUtils.d.ts +2 -0
  65. package/build/dts/utils/Console.d.ts +2 -0
  66. package/build/dts/utils/ThrottleUtils.d.ts +2 -0
  67. package/build/dts/vibration/VibrationManager.d.ts +19 -2
  68. package/build/index.d.ts +292 -62
  69. package/build/index.node.d.ts +286 -59
  70. package/examples/3d/scene.html +19 -5
  71. package/examples/3d-generic/index.html +144 -0
  72. package/examples/3d-generic/script.js +266 -0
  73. package/examples/basic/index.html +267 -17
  74. package/examples/basic/script.js +958 -105
  75. package/examples/camera/barcode-detector.js +109 -0
  76. package/examples/camera/depth-estimation.js +71 -0
  77. package/examples/camera/face-detector.js +119 -0
  78. package/examples/camera/face-landmark.js +111 -0
  79. package/examples/camera/gesture-recognition.js +97 -0
  80. package/examples/camera/hand-landmark.js +74 -0
  81. package/examples/camera/image-segmentation.js +98 -0
  82. package/examples/camera/image-to-text.js +43 -0
  83. package/examples/camera/image-upscale.js +75 -0
  84. package/examples/camera/index.html +129 -0
  85. package/examples/camera/object-detection.js +98 -0
  86. package/examples/camera/pose-landmark.js +60 -0
  87. package/examples/camera/script.js +316 -0
  88. package/examples/camera/utils.js +165 -0
  89. package/examples/camera/yolo-tiny.js +54 -0
  90. package/examples/camera/yolo.js +119 -0
  91. package/examples/edge-impulse/script.js +157 -48
  92. package/examples/edge-impulse-test/README.md +11 -0
  93. package/examples/edge-impulse-test/edge-impulse-standalone.js +7228 -0
  94. package/examples/edge-impulse-test/edge-impulse-standalone.wasm +0 -0
  95. package/examples/edge-impulse-test/index.html +75 -0
  96. package/examples/edge-impulse-test/run-impulse.js +135 -0
  97. package/examples/edge-impulse-test/script.js +200 -0
  98. package/examples/glasses-gestures/README.md +11 -0
  99. package/examples/glasses-gestures/edge-impulse-standalone.js +7228 -0
  100. package/examples/glasses-gestures/edge-impulse-standalone.wasm +0 -0
  101. package/examples/glasses-gestures/index.html +69 -0
  102. package/examples/glasses-gestures/run-impulse.js +135 -0
  103. package/examples/glasses-gestures/script.js +226 -0
  104. package/examples/gloves/edge-impulse-standalone.js +7228 -0
  105. package/examples/gloves/edge-impulse-standalone.wasm +0 -0
  106. package/examples/gloves/index.html +4 -1
  107. package/examples/gloves/run-impulse.js +135 -0
  108. package/examples/gloves/script.js +367 -51
  109. package/examples/graph/script.js +94 -37
  110. package/examples/microphone/gender.js +54 -0
  111. package/examples/microphone/index.html +102 -0
  112. package/examples/microphone/script.js +394 -0
  113. package/examples/microphone/utils.js +45 -0
  114. package/examples/microphone/whisper-realtime.js +166 -0
  115. package/examples/microphone/whisper.js +132 -0
  116. package/examples/punch/index.html +135 -0
  117. package/examples/punch/punch.tflite +0 -0
  118. package/examples/punch/script.js +169 -0
  119. package/examples/server/index.html +98 -22
  120. package/examples/server/script.js +317 -109
  121. package/examples/ukaton-firmware-update/merged-firmware.bin +0 -0
  122. package/examples/utils/aframe/aframe-master.min.js +2 -0
  123. package/examples/utils/aframe/bs-vibration.js +150 -0
  124. package/examples/utils/aframe/force-pushable.js +80 -0
  125. package/examples/utils/aframe/grabbable-anchor.js +46 -0
  126. package/examples/utils/aframe/grabbable-listener.js +31 -0
  127. package/examples/utils/aframe/grabbable-physics-body.js +190 -0
  128. package/examples/utils/aframe/grow-shrink.js +25 -0
  129. package/examples/utils/aframe/hand-punch.js +119 -0
  130. package/examples/utils/aframe/my-obb-collider.js +293 -0
  131. package/examples/utils/aframe/occlude-hand-tracking-controls.js +47 -0
  132. package/examples/utils/aframe/occlude-mesh.js +42 -0
  133. package/examples/utils/aframe/palm-up-detector.js +47 -0
  134. package/examples/utils/aframe/shadow-material.js +20 -0
  135. package/examples/utils/aframe/soft-shadow-light.js +9 -0
  136. package/examples/webxr-2/assets/3d/soccerBall.glb +0 -0
  137. package/examples/webxr-2/assets/audio/shellBounce.wav +0 -0
  138. package/examples/webxr-2/assets/audio/shellHit.wav +0 -0
  139. package/examples/webxr-2/assets/audio/shellKick.wav +0 -0
  140. package/examples/webxr-2/assets/audio/soccerBounce.wav +0 -0
  141. package/examples/webxr-2/assets/audio/soccerKick.mp3 +0 -0
  142. package/examples/webxr-2/assets/images/shellTexture.png +0 -0
  143. package/examples/webxr-2/components/bs-ankle.js +337 -0
  144. package/examples/webxr-2/components/coin.js +84 -0
  145. package/examples/webxr-2/components/custom-wrap.js +17 -0
  146. package/examples/webxr-2/components/goomba.js +3250 -0
  147. package/examples/webxr-2/components/init-shell-material.js +215 -0
  148. package/examples/webxr-2/components/platter.js +172 -0
  149. package/examples/webxr-2/components/shell.js +374 -0
  150. package/examples/webxr-2/components/soccer-ball.js +250 -0
  151. package/examples/webxr-2/components/squashed-goomba.js +249 -0
  152. package/examples/webxr-2/edge-impulse-standalone.js +7228 -0
  153. package/examples/webxr-2/edge-impulse-standalone.wasm +0 -0
  154. package/examples/webxr-2/index.html +996 -0
  155. package/examples/webxr-2/kick.tflite +0 -0
  156. package/examples/webxr-2/kick2.tflite +0 -0
  157. package/examples/webxr-2/run-impulse.js +135 -0
  158. package/examples/webxr-2/script.js +384 -0
  159. package/examples/webxr-3/components/bs-camera.js +65 -0
  160. package/examples/webxr-3/index.html +134 -0
  161. package/examples/webxr-3/script.js +432 -0
  162. package/package.json +2 -1
  163. package/src/.prettierrc +4 -0
  164. package/src/BS.ts +79 -8
  165. package/src/CameraManager.ts +497 -0
  166. package/src/Device.ts +691 -86
  167. package/src/DeviceInformationManager.ts +19 -10
  168. package/src/DeviceManager.ts +85 -25
  169. package/src/FileTransferManager.ts +145 -20
  170. package/src/InformationManager.ts +40 -15
  171. package/src/MicrophoneManager.ts +599 -0
  172. package/src/TfliteManager.ts +171 -25
  173. package/src/WifiManager.ts +323 -0
  174. package/src/connection/BaseConnectionManager.ts +130 -30
  175. package/src/connection/ClientConnectionManager.ts +34 -10
  176. package/src/connection/bluetooth/BluetoothConnectionManager.ts +8 -2
  177. package/src/connection/bluetooth/NobleConnectionManager.ts +147 -41
  178. package/src/connection/bluetooth/WebBluetoothConnectionManager.ts +99 -34
  179. package/src/connection/bluetooth/bluetoothUUIDs.ts +40 -13
  180. package/src/connection/udp/UDPConnectionManager.ts +356 -0
  181. package/src/connection/websocket/WebSocketConnectionManager.ts +282 -0
  182. package/src/devicePair/DevicePair.ts +95 -25
  183. package/src/devicePair/DevicePairPressureSensorDataManager.ts +27 -7
  184. package/src/scanner/BaseScanner.ts +49 -11
  185. package/src/scanner/NobleScanner.ts +76 -14
  186. package/src/sensor/MotionSensorDataManager.ts +21 -6
  187. package/src/sensor/PressureSensorDataManager.ts +37 -8
  188. package/src/sensor/SensorConfigurationManager.ts +73 -22
  189. package/src/sensor/SensorDataManager.ts +109 -23
  190. package/src/server/BaseClient.ts +150 -36
  191. package/src/server/BaseServer.ts +50 -2
  192. package/src/server/ServerUtils.ts +39 -9
  193. package/src/server/udp/UDPServer.ts +73 -22
  194. package/src/server/udp/UDPUtils.ts +9 -2
  195. package/src/server/websocket/WebSocketClient.ts +27 -7
  196. package/src/server/websocket/WebSocketUtils.ts +4 -2
  197. package/src/utils/AudioUtils.ts +65 -0
  198. package/src/utils/Console.ts +62 -9
  199. package/src/utils/ParseUtils.ts +24 -5
  200. package/src/utils/ThrottleUtils.ts +62 -0
  201. package/src/utils/Timer.ts +1 -1
  202. package/src/vibration/VibrationManager.ts +166 -40
@@ -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;
@@ -0,0 +1,282 @@
1
+ import { DeviceInformationTypes } from "../../DeviceInformationManager.ts";
2
+ import {
3
+ createMessage,
4
+ Message,
5
+ MessageLike,
6
+ } from "../../server/ServerUtils.ts";
7
+ import { webSocketPingTimeout } from "../../server/websocket/WebSocketUtils.ts";
8
+ import { createConsole } from "../../utils/Console.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
+ import type * as ws from "ws";
19
+
20
+ const _console = createConsole("WebSocketConnectionManager", { log: false });
21
+
22
+ const WebSocketMessageTypes = [
23
+ "ping",
24
+ "pong",
25
+ "batteryLevel",
26
+ "deviceInformation",
27
+ "message",
28
+ ] as const;
29
+ type WebSocketMessageType = (typeof WebSocketMessageTypes)[number];
30
+
31
+ type WebSocketMessage = WebSocketMessageType | Message<WebSocketMessageType>;
32
+ function createWebSocketMessage(...messages: WebSocketMessage[]) {
33
+ _console.log("createWebSocketMessage", ...messages);
34
+ return createMessage(WebSocketMessageTypes, ...messages);
35
+ }
36
+
37
+ const WebSocketDeviceInformationMessageTypes: WebSocketMessageType[] = [
38
+ "deviceInformation",
39
+ "batteryLevel",
40
+ ];
41
+
42
+ class WebSocketConnectionManager extends BaseConnectionManager {
43
+ #bluetoothId?: string;
44
+ get bluetoothId() {
45
+ return this.#bluetoothId ?? "";
46
+ }
47
+
48
+ defaultMtu = 2 ** 10;
49
+
50
+ constructor(
51
+ ipAddress: string,
52
+ isSecure: boolean = false,
53
+ bluetoothId?: string
54
+ ) {
55
+ super();
56
+ this.ipAddress = ipAddress;
57
+ this.isSecure = isSecure;
58
+ this.mtu = this.defaultMtu;
59
+ this.#bluetoothId = bluetoothId;
60
+ }
61
+
62
+ get isAvailable() {
63
+ return true;
64
+ }
65
+
66
+ static get isSupported() {
67
+ return true;
68
+ }
69
+ static get type(): ConnectionType {
70
+ return "webSocket";
71
+ }
72
+
73
+ // WEBSOCKET
74
+ #webSocket?: WebSocket;
75
+ get webSocket() {
76
+ return this.#webSocket;
77
+ }
78
+ set webSocket(newWebSocket) {
79
+ if (this.#webSocket == newWebSocket) {
80
+ _console.log("redundant webSocket assignment");
81
+ return;
82
+ }
83
+
84
+ _console.log("assigning webSocket", newWebSocket);
85
+
86
+ if (this.#webSocket) {
87
+ removeEventListeners(this.#webSocket, this.#boundWebSocketEventListeners);
88
+ if (this.#webSocket.readyState == this.#webSocket.OPEN) {
89
+ this.#webSocket.close();
90
+ }
91
+ }
92
+
93
+ if (newWebSocket) {
94
+ addEventListeners(newWebSocket, this.#boundWebSocketEventListeners);
95
+ }
96
+ this.#webSocket = newWebSocket;
97
+
98
+ _console.log("assigned webSocket");
99
+ }
100
+
101
+ // IP ADDRESS
102
+ #ipAddress!: string;
103
+ get ipAddress() {
104
+ return this.#ipAddress;
105
+ }
106
+ set ipAddress(newIpAddress) {
107
+ this.assertIsNotConnected();
108
+ if (this.#ipAddress == newIpAddress) {
109
+ _console.log(`redundnant ipAddress assignment "${newIpAddress}"`);
110
+ return;
111
+ }
112
+ this.#ipAddress = newIpAddress;
113
+ _console.log(`updated ipAddress to "${this.ipAddress}"`);
114
+ }
115
+
116
+ // IS SECURE
117
+ #isSecure = false;
118
+ get isSecure() {
119
+ return this.#isSecure;
120
+ }
121
+ set isSecure(newIsSecure) {
122
+ this.assertIsNotConnected();
123
+ if (this.#isSecure == newIsSecure) {
124
+ _console.log(`redundant isSecure assignment ${newIsSecure}`);
125
+ return;
126
+ }
127
+ this.#isSecure = newIsSecure;
128
+ _console.log(`updated isSecure to "${this.isSecure}"`);
129
+ }
130
+
131
+ // URL
132
+ get url() {
133
+ return `${this.isSecure ? "wss" : "ws"}://${this.ipAddress}/ws`;
134
+ }
135
+
136
+ // CONNECTION
137
+ async connect() {
138
+ await super.connect();
139
+ try {
140
+ this.webSocket = new WebSocket(this.url);
141
+ } catch (error) {
142
+ _console.error("error connecting to webSocket", error);
143
+ this.status = "notConnected";
144
+ }
145
+ }
146
+ async disconnect() {
147
+ await super.disconnect();
148
+ _console.log("closing websocket");
149
+ this.#pingTimer.stop();
150
+ this.#webSocket?.close();
151
+ }
152
+
153
+ get canReconnect() {
154
+ return Boolean(this.webSocket);
155
+ }
156
+ async reconnect() {
157
+ await super.reconnect();
158
+ this.webSocket = new WebSocket(this.url);
159
+ }
160
+
161
+ // BASE CONNECTION MANAGER
162
+ async sendSmpMessage(data: ArrayBuffer) {
163
+ super.sendSmpMessage(data);
164
+ _console.error("smp not supported on webSockets");
165
+ }
166
+
167
+ async sendTxData(data: ArrayBuffer) {
168
+ await super.sendTxData(data);
169
+ if (data.byteLength == 0) {
170
+ return;
171
+ }
172
+ this.#sendWebSocketMessage({ type: "message", data });
173
+ }
174
+
175
+ // WEBSOCKET MESSAGING
176
+ #sendMessage(message: MessageLike) {
177
+ this.assertIsConnected();
178
+ _console.log("sending webSocket message", message);
179
+ this.#webSocket!.send(message);
180
+ this.#pingTimer.restart();
181
+ }
182
+
183
+ #sendWebSocketMessage(...messages: WebSocketMessage[]) {
184
+ this.#sendMessage(createWebSocketMessage(...messages));
185
+ }
186
+
187
+ // WEBSOCKET EVENTS
188
+ #boundWebSocketEventListeners: { [eventType: string]: Function } = {
189
+ open: this.#onWebSocketOpen.bind(this),
190
+ message: this.#onWebSocketMessage.bind(this),
191
+ close: this.#onWebSocketClose.bind(this),
192
+ error: this.#onWebSocketError.bind(this),
193
+ };
194
+
195
+ #onWebSocketOpen(event: ws.Event) {
196
+ _console.log("webSocket.open", event);
197
+ this.#pingTimer.start();
198
+ this.status = "connected";
199
+ this.#requestDeviceInformation();
200
+ }
201
+ async #onWebSocketMessage(event: ws.MessageEvent) {
202
+ // this.#pingTimer.restart();
203
+ //@ts-expect-error
204
+ const arrayBuffer = await event.data.arrayBuffer();
205
+ const dataView = new DataView(arrayBuffer);
206
+ _console.log(`webSocket.message (${dataView.byteLength} bytes)`);
207
+ this.#parseWebSocketMessage(dataView);
208
+ }
209
+ #onWebSocketClose(event: ws.CloseEvent) {
210
+ _console.log("webSocket.close", event);
211
+ this.status = "notConnected";
212
+ this.#pingTimer.stop();
213
+ }
214
+ #onWebSocketError(event: ws.ErrorEvent) {
215
+ _console.error("webSocket.error", event);
216
+ }
217
+
218
+ // PARSING
219
+ #parseWebSocketMessage(dataView: DataView) {
220
+ parseMessage(
221
+ dataView,
222
+ WebSocketMessageTypes,
223
+ this.#onMessage.bind(this),
224
+ null,
225
+ true
226
+ );
227
+ }
228
+
229
+ #onMessage(messageType: WebSocketMessageType, dataView: DataView) {
230
+ _console.log(
231
+ `received "${messageType}" message (${dataView.byteLength} bytes)`
232
+ );
233
+ switch (messageType) {
234
+ case "ping":
235
+ this.#pong();
236
+ break;
237
+ case "pong":
238
+ break;
239
+ case "batteryLevel":
240
+ this.onMessageReceived?.("batteryLevel", dataView);
241
+ break;
242
+ case "deviceInformation":
243
+ parseMessage(
244
+ dataView,
245
+ DeviceInformationTypes,
246
+ (deviceInformationType, dataView) => {
247
+ this.onMessageReceived!(deviceInformationType, dataView);
248
+ }
249
+ );
250
+ break;
251
+ case "message":
252
+ this.parseRxMessage(dataView);
253
+ break;
254
+ default:
255
+ _console.error(`uncaught messageType "${messageType}"`);
256
+ break;
257
+ }
258
+ }
259
+
260
+ // PING
261
+ #pingTimer = new Timer(this.#ping.bind(this), webSocketPingTimeout - 1_000);
262
+ #ping() {
263
+ _console.log("pinging");
264
+ this.#sendWebSocketMessage("ping");
265
+ }
266
+ #pong() {
267
+ _console.log("ponging");
268
+ this.#sendWebSocketMessage("pong");
269
+ }
270
+
271
+ // DEVICE INFORMATION
272
+ #requestDeviceInformation() {
273
+ this.#sendWebSocketMessage(...WebSocketDeviceInformationMessageTypes);
274
+ }
275
+
276
+ remove() {
277
+ super.remove();
278
+ this.webSocket = undefined;
279
+ }
280
+ }
281
+
282
+ export default WebSocketConnectionManager;