ota-hub-reactjs 0.0.18 → 0.1.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.
@@ -1,10 +1,12 @@
1
1
  import { useEffect, useRef } from "react";
2
- import { MultiDeviceWhisperer } from "../base/device-whisperer.js";
2
+ import { useMultiDeviceWhisperer, } from "../base/device-whisperer.js";
3
3
  import mqtt from "mqtt";
4
- export function MQTTMultiDeviceWhisperer({ serverUrl, uuidFromMessage, subTopicFromUuid = undefined, pubTopicFromUuid = undefined, serverPort = 8883, clientId = undefined, username = undefined, password = undefined, serverAutoConnect = true, serverConnectOn = false, ...props }) {
5
- const base = MultiDeviceWhisperer(props);
4
+ export function useMQTTMultiDeviceWhisperer({ serverUrl, serverUrlFnc, uuidFromMessage, subTopicFromUuid = undefined, pubTopicFromUuid = undefined, serverPort = 443, clientId = undefined, username = undefined, password = undefined, serverAutoConnect = true, serverConnectOn = false, enableWatchdog = true, ...props }) {
5
+ const base = useMultiDeviceWhisperer(props);
6
6
  const clientRef = useRef(null);
7
7
  const isUnmountedRef = useRef(false);
8
+ // ✨ NEW: Manage our own reconnect timer
9
+ const reconnectTimeoutRef = useRef(null);
8
10
  const watchdogTimers = useRef({});
9
11
  const addingConnections = useRef(new Set());
10
12
  const topicCallbacksRef = useRef(new Map());
@@ -84,47 +86,71 @@ export function MQTTMultiDeviceWhisperer({ serverUrl, uuidFromMessage, subTopicF
84
86
  }
85
87
  };
86
88
  };
87
- const connectToMQTTServer = () => {
89
+ const connectToMQTTServer = async () => {
90
+ if (!serverUrl && !serverUrlFnc) {
91
+ console.error("MQTT useMultiDeviceWhisperer requires either serverUrl or serverUrlFnc");
92
+ return;
93
+ }
94
+ if (reconnectTimeoutRef.current)
95
+ clearTimeout(reconnectTimeoutRef.current);
88
96
  isUnmountedRef.current = false;
89
97
  if (clientRef.current) {
90
98
  if (clientRef.current.connected) {
91
99
  base.setIsReady(true);
92
100
  return;
93
101
  }
102
+ clientRef.current.removeAllListeners();
94
103
  clientRef.current.end(true);
95
104
  }
96
105
  try {
97
- const new_client = mqtt.connect(serverUrl, {
106
+ const finalUrl = serverUrlFnc ? await serverUrlFnc() : serverUrl;
107
+ if (!finalUrl)
108
+ throw new Error("Generated MQTT URL was undefined.");
109
+ const options = {
98
110
  port: serverPort,
99
111
  clientId: clientId,
100
112
  username,
101
113
  password,
102
114
  clean: true,
103
115
  keepalive: 30,
104
- reconnectPeriod: 3000,
105
- });
116
+ reconnectPeriod: 0, // We now handle this ourselves
117
+ };
118
+ const new_client = mqtt.connect(finalUrl, options);
106
119
  new_client.on("connect", () => {
107
120
  console.log("MQTT Whisperer Connected");
108
121
  base.setIsReady(true);
109
- // Re-subscribe custom topic callbacks after reconnect.
122
+ // Re-subscribe topic callbacks
110
123
  topicCallbacksRef.current.forEach((_callbacks, topic) => {
111
124
  new_client.subscribe(topic, { qos: 1 }, (err) => {
112
125
  if (err)
113
126
  console.error("Topic callback subscribe failed:", err);
114
127
  });
115
128
  });
129
+ // Since we create a brand new client on reconnect, we MUST re-subscribe
130
+ // to all actively managed device connections.
131
+ base.connections.forEach((conn) => {
132
+ const topic = subTopicFromUuid?.(conn.uuid) ?? conn.uuid;
133
+ new_client.subscribe(topic, { qos: 1 });
134
+ });
116
135
  });
117
- new_client.on("reconnect", () => {
118
- console.log("MQTT Whisperer Reconnecting...");
119
- base.setIsReady(false);
120
- });
121
- new_client.on("close", () => {
122
- console.log("MQTT Whisperer Closed");
136
+ const handleReconnectCycle = () => {
123
137
  base.setIsReady(false);
124
- });
138
+ if (isUnmountedRef.current)
139
+ return;
140
+ console.log("MQTT Whisperer closed. Scheduling async reconnect...");
141
+ // Ensure we only queue one reconnect attempt
142
+ if (reconnectTimeoutRef.current)
143
+ clearTimeout(reconnectTimeoutRef.current);
144
+ if (serverAutoConnect || serverConnectOn) {
145
+ reconnectTimeoutRef.current = setTimeout(() => {
146
+ connectToMQTTServer();
147
+ }, 3000); // 3 second backoff
148
+ }
149
+ };
150
+ new_client.on("close", handleReconnectCycle);
125
151
  new_client.on("error", (err) => {
126
- base.setIsReady(false);
127
152
  console.error("MQTT Whisperer Error:", err);
153
+ handleReconnectCycle();
128
154
  });
129
155
  new_client.on("message", (topic, payload) => {
130
156
  if (isUnmountedRef.current)
@@ -155,26 +181,13 @@ export function MQTTMultiDeviceWhisperer({ serverUrl, uuidFromMessage, subTopicF
155
181
  catch (err) {
156
182
  console.error("MQTT init failed:", err);
157
183
  base.setIsReady(false);
158
- }
159
- return () => {
160
- isUnmountedRef.current = true;
161
- base.setIsReady(false);
162
- Object.keys(watchdogTimers.current).forEach((uuid) => {
163
- const timers = watchdogTimers.current[uuid];
164
- if (timers) {
165
- clearTimeout(timers.ping);
166
- clearTimeout(timers.warn);
167
- clearTimeout(timers.fail);
168
- }
169
- });
170
- watchdogTimers.current = {};
171
- topicCallbacksRef.current.clear();
172
- if (clientRef.current) {
173
- clientRef.current.removeAllListeners();
174
- clientRef.current.end(true);
184
+ // If fetching the URL fails, try again in 3 seconds
185
+ if (!isUnmountedRef.current && (serverAutoConnect || serverConnectOn)) {
186
+ reconnectTimeoutRef.current = setTimeout(() => {
187
+ connectToMQTTServer();
188
+ }, 3000);
175
189
  }
176
- clientRef.current = null;
177
- };
190
+ }
178
191
  };
179
192
  const connect = async (uuid) => {
180
193
  const conn = base.getConnection(uuid);
@@ -200,7 +213,8 @@ export function MQTTMultiDeviceWhisperer({ serverUrl, uuidFromMessage, subTopicF
200
213
  return;
201
214
  const topic = subTopicFromUuid?.(uuid) ?? uuid;
202
215
  // Keep topic subscribed if callbacks or other device-connections still depend on it.
203
- if (hasCallbackForTopic(topic) || hasOtherConnectionUsingTopic(topic, uuid)) {
216
+ if (hasCallbackForTopic(topic) ||
217
+ hasOtherConnectionUsingTopic(topic, uuid)) {
204
218
  return;
205
219
  }
206
220
  if (clientRef.current.connected && !clientRef.current.disconnecting) {
@@ -213,12 +227,12 @@ export function MQTTMultiDeviceWhisperer({ serverUrl, uuidFromMessage, subTopicF
213
227
  });
214
228
  }
215
229
  else {
216
- console.warn("Skipped subscribe - client disconnected or unmounted");
230
+ console.warn("Skipped unsubscribe - client disconnected or unmounted");
217
231
  }
218
232
  };
219
233
  function touchHeartbeat(uuid) {
220
- if (isUnmountedRef.current)
221
- return; // Stop if unmounted
234
+ if (isUnmountedRef.current || !enableWatchdog)
235
+ return;
222
236
  clearTimeout(watchdogTimers.current[uuid]?.ping);
223
237
  clearTimeout(watchdogTimers.current[uuid]?.warn);
224
238
  clearTimeout(watchdogTimers.current[uuid]?.fail);
@@ -226,7 +240,7 @@ export function MQTTMultiDeviceWhisperer({ serverUrl, uuidFromMessage, subTopicF
226
240
  base.updateConnection(uuid, (c) => ({
227
241
  ...c,
228
242
  isConnected: true,
229
- isConnecting: false
243
+ isConnecting: false,
230
244
  }));
231
245
  const ping = setTimeout(() => {
232
246
  if (isUnmountedRef.current)
@@ -236,12 +250,20 @@ export function MQTTMultiDeviceWhisperer({ serverUrl, uuidFromMessage, subTopicF
236
250
  const warn = setTimeout(() => {
237
251
  if (isUnmountedRef.current)
238
252
  return;
239
- base.updateConnection(uuid, (c) => ({ ...c, isConnected: false, isConnecting: true }));
253
+ base.updateConnection(uuid, (c) => ({
254
+ ...c,
255
+ isConnected: false,
256
+ isConnecting: true,
257
+ }));
240
258
  }, 30000);
241
259
  const fail = setTimeout(() => {
242
260
  if (isUnmountedRef.current)
243
261
  return;
244
- base.updateConnection(uuid, (c) => ({ ...c, isConnected: false, isConnecting: false }));
262
+ base.updateConnection(uuid, (c) => ({
263
+ ...c,
264
+ isConnected: false,
265
+ isConnecting: false,
266
+ }));
245
267
  }, 60000);
246
268
  watchdogTimers.current[uuid] = { ping, warn, fail };
247
269
  }
@@ -274,15 +296,16 @@ export function MQTTMultiDeviceWhisperer({ serverUrl, uuidFromMessage, subTopicF
274
296
  const topic = pubTopicFromUuid?.(uuid) ?? subTopicFromUuid?.(uuid) ?? uuid;
275
297
  await publish(topic, data, uuid);
276
298
  };
277
- const addConnection = async ({ uuid, propCreator }) => {
299
+ const addConnection = async ({ uuid, propCreator, }) => {
278
300
  if (!clientRef.current || isUnmountedRef.current)
279
- return;
301
+ return "";
280
302
  if (!uuid) {
281
303
  Error("In MQTT you MUST define a UUID otherwise we don't know what device we're connecting to!");
282
- return;
304
+ return "";
283
305
  }
284
- if (base.connections.some(c => c.uuid === uuid) || addingConnections.current.has(uuid)) {
285
- return;
306
+ if (base.connections.some((c) => c.uuid === uuid) ||
307
+ addingConnections.current.has(uuid)) {
308
+ return "";
286
309
  }
287
310
  await base.addConnection({
288
311
  uuid,
@@ -297,12 +320,10 @@ export function MQTTMultiDeviceWhisperer({ serverUrl, uuidFromMessage, subTopicF
297
320
  touchHeartbeat: () => touchHeartbeat(id),
298
321
  // Initial connection state
299
322
  ...base.createInitialConnectionState(id),
300
- // From props
301
- ...props
323
+ ...props,
302
324
  };
303
- }
325
+ },
304
326
  });
305
- // Delete this adding connections item
306
327
  addingConnections.current.delete(uuid);
307
328
  // Connect immediately
308
329
  const conn = base.getConnection(uuid);
@@ -315,7 +336,7 @@ export function MQTTMultiDeviceWhisperer({ serverUrl, uuidFromMessage, subTopicF
315
336
  base.removeConnection(uuid);
316
337
  };
317
338
  const reconnectAll = async () => {
318
- const connectionIds = base.connections.map(c => c.uuid);
339
+ const connectionIds = base.connections.map((c) => c.uuid);
319
340
  await Promise.all(connectionIds.map(async (id) => {
320
341
  const c = base.getConnection(id);
321
342
  if (!c)
@@ -328,8 +349,27 @@ export function MQTTMultiDeviceWhisperer({ serverUrl, uuidFromMessage, subTopicF
328
349
  useEffect(() => {
329
350
  if (!(serverAutoConnect || serverConnectOn))
330
351
  return;
331
- const cleanup = connectToMQTTServer();
332
- return cleanup;
352
+ connectToMQTTServer();
353
+ return () => {
354
+ isUnmountedRef.current = true;
355
+ if (reconnectTimeoutRef.current)
356
+ clearTimeout(reconnectTimeoutRef.current);
357
+ Object.keys(watchdogTimers.current).forEach((uuid) => {
358
+ const timers = watchdogTimers.current[uuid];
359
+ if (timers) {
360
+ clearTimeout(timers.ping);
361
+ clearTimeout(timers.warn);
362
+ clearTimeout(timers.fail);
363
+ }
364
+ });
365
+ watchdogTimers.current = {};
366
+ topicCallbacksRef.current.clear();
367
+ if (clientRef.current) {
368
+ clientRef.current.removeAllListeners();
369
+ clientRef.current.end(true);
370
+ }
371
+ clientRef.current = null;
372
+ };
333
373
  }, [serverUrl, serverConnectOn]);
334
374
  return {
335
375
  ...base,
@@ -338,6 +378,6 @@ export function MQTTMultiDeviceWhisperer({ serverUrl, uuidFromMessage, subTopicF
338
378
  connect,
339
379
  disconnect,
340
380
  reconnectAll,
341
- connectToMQTTServer
381
+ connectToMQTTServer,
342
382
  };
343
383
  }
@@ -13,8 +13,8 @@ export declare class UsbTransport {
13
13
  */
14
14
  connect(baudRate?: number): Promise<void>;
15
15
  /**
16
- * Writes bytes to the device
17
- */
16
+ * Writes bytes to the device
17
+ */
18
18
  write(data: Uint8Array): Promise<void>;
19
19
  /**
20
20
  * Reads bytes from the device.
@@ -38,8 +38,8 @@ export type SerialConnectionState = DeviceConnectionState & {
38
38
  baudrate?: number;
39
39
  slipReadWrite?: boolean;
40
40
  };
41
- export declare function SerialMultiDeviceWhisperer<AppOrMessageLayer extends SerialConnectionState>({ ...props }?: DeviceWhispererProps<AppOrMessageLayer>): {
42
- addConnection: ({ uuid, propCreator }: AddConnectionProps<AppOrMessageLayer>) => Promise<string | undefined>;
41
+ export declare function useSerialMultiDeviceWhisperer<AppOrMessageLayer extends SerialConnectionState>({ ...props }?: DeviceWhispererProps<AppOrMessageLayer>): {
42
+ addConnection: ({ uuid, propCreator, }: AddConnectionProps<AppOrMessageLayer>) => Promise<string | undefined>;
43
43
  removeConnection: (uuid: string) => Promise<void>;
44
44
  connect: (uuid: string, baudrate?: number) => Promise<void>;
45
45
  disconnect: (uuid: string) => Promise<void>;
@@ -1,4 +1,4 @@
1
- import { MultiDeviceWhisperer } from "../base/device-whisperer.js";
1
+ import { useMultiDeviceWhisperer, } from "../base/device-whisperer.js";
2
2
  import { useEffect, useRef, useState } from "react";
3
3
  /*
4
4
  ┌────────────────────────────────┐
@@ -31,16 +31,19 @@ export class UsbTransport {
31
31
  const interfaces = this.device.configuration?.interfaces || [];
32
32
  let ctrlIface;
33
33
  let dataIface;
34
- dataIface = interfaces.find(iface => {
34
+ dataIface = interfaces.find((iface) => {
35
35
  const endpoints = iface.alternate.endpoints;
36
- return endpoints.some(e => e.direction === 'in' && e.type === 'bulk') &&
37
- endpoints.some(e => e.direction === 'out' && e.type === 'bulk');
36
+ return (endpoints.some((e) => e.direction === "in" && e.type === "bulk") &&
37
+ endpoints.some((e) => e.direction === "out" && e.type === "bulk"));
38
38
  });
39
39
  if (dataIface) {
40
40
  this.dataInterface = dataIface.interfaceNumber;
41
- ctrlIface = interfaces.find(i => i.interfaceNumber === this.dataInterface - 1)
42
- || interfaces.find(i => i.alternate.interfaceClass === 2);
43
- this.controlInterface = ctrlIface ? ctrlIface.interfaceNumber : this.dataInterface;
41
+ ctrlIface =
42
+ interfaces.find((i) => i.interfaceNumber === this.dataInterface - 1) ||
43
+ interfaces.find((i) => i.alternate.interfaceClass === 2);
44
+ this.controlInterface = ctrlIface
45
+ ? ctrlIface.interfaceNumber
46
+ : this.dataInterface;
44
47
  }
45
48
  if (!dataIface) {
46
49
  throw new Error("No serial-compatible Bulk interface found.");
@@ -62,14 +65,14 @@ export class UsbTransport {
62
65
  throw new Error(`Failed to claim Data Interface. Android OS has locked the device driver. Try using a 'Vendor Specific' USB Class device or a native Serial App workaround.`);
63
66
  }
64
67
  const endpoints = dataIface.alternate.endpoints;
65
- this.endpointIn = endpoints.find(e => e.direction === 'in' && e.type === 'bulk').endpointNumber;
66
- this.endpointOut = endpoints.find(e => e.direction === 'out' && e.type === 'bulk').endpointNumber;
68
+ this.endpointIn = endpoints.find((e) => e.direction === "in" && e.type === "bulk").endpointNumber;
69
+ this.endpointOut = endpoints.find((e) => e.direction === "out" && e.type === "bulk").endpointNumber;
67
70
  await this.setBaudRate(baudRate);
68
71
  await this.setSignals({ dtr: false, rts: false });
69
72
  }
70
73
  /**
71
- * Writes bytes to the device
72
- */
74
+ * Writes bytes to the device
75
+ */
73
76
  async write(data) {
74
77
  if (!this.device.opened)
75
78
  return;
@@ -84,7 +87,7 @@ export class UsbTransport {
84
87
  return null;
85
88
  try {
86
89
  const result = await this.device.transferIn(this.endpointIn, length);
87
- if (result.status === 'ok' && result.data) {
90
+ if (result.status === "ok" && result.data) {
88
91
  return new Uint8Array(result.data.buffer);
89
92
  }
90
93
  }
@@ -100,13 +103,13 @@ export class UsbTransport {
100
103
  async setSignals({ dtr, rts }) {
101
104
  if (!this.device.opened)
102
105
  return;
103
- const value = (Number(dtr) | (Number(rts) << 1));
106
+ const value = Number(dtr) | (Number(rts) << 1);
104
107
  await this.device.controlTransferOut({
105
- requestType: 'class',
106
- recipient: 'interface',
108
+ requestType: "class",
109
+ recipient: "interface",
107
110
  request: UsbTransport.SET_CONTROL_LINE_STATE,
108
111
  value: value,
109
- index: this.controlInterface
112
+ index: this.controlInterface,
110
113
  });
111
114
  }
112
115
  async setBaudRate(baud) {
@@ -121,11 +124,11 @@ export class UsbTransport {
121
124
  view.setUint8(5, 0);
122
125
  view.setUint8(6, 8);
123
126
  await this.device.controlTransferOut({
124
- requestType: 'class',
125
- recipient: 'interface',
127
+ requestType: "class",
128
+ recipient: "interface",
126
129
  request: UsbTransport.SET_LINE_CODING,
127
130
  value: 0,
128
- index: this.controlInterface
131
+ index: this.controlInterface,
129
132
  }, buffer);
130
133
  }
131
134
  async disconnect() {
@@ -148,8 +151,8 @@ export class UsbTransport {
148
151
  // Standard CDC-ACM requests
149
152
  UsbTransport.SET_LINE_CODING = 0x20;
150
153
  UsbTransport.SET_CONTROL_LINE_STATE = 0x22;
151
- export function SerialMultiDeviceWhisperer({ ...props } = {}) {
152
- const base = MultiDeviceWhisperer(props);
154
+ export function useSerialMultiDeviceWhisperer({ ...props } = {}) {
155
+ const base = useMultiDeviceWhisperer(props);
153
156
  const releasePortByDefaultRef = useRef(false);
154
157
  const [releasePortByDefaultState, setReleasePortByDefaultState] = useState(false);
155
158
  useEffect(() => {
@@ -171,7 +174,10 @@ export function SerialMultiDeviceWhisperer({ ...props } = {}) {
171
174
  const asText = decoder.decode(bytes);
172
175
  const combined = conn.readBufferLeftover + asText;
173
176
  const lines = combined.split("\n");
174
- base.updateConnection(uuid, (c) => ({ ...c, readBufferLeftover: lines.pop() || "" }));
177
+ base.updateConnection(uuid, (c) => ({
178
+ ...c,
179
+ readBufferLeftover: lines.pop() || "",
180
+ }));
175
181
  for (const line of lines) {
176
182
  if (line.trim()) {
177
183
  base.appendLog(uuid, { level: 2, message: line.trim() });
@@ -204,7 +210,7 @@ export function SerialMultiDeviceWhisperer({ ...props } = {}) {
204
210
  const bytes = await transport.read(64);
205
211
  if (!bytes || bytes.length === 0) {
206
212
  // Small delay to prevent CPU spinning if device sends nothing
207
- await new Promise(r => setTimeout(r, 10));
213
+ await new Promise((r) => setTimeout(r, 10));
208
214
  continue;
209
215
  }
210
216
  // --- Same Decoding Logic as before ---
@@ -212,7 +218,7 @@ export function SerialMultiDeviceWhisperer({ ...props } = {}) {
212
218
  const b = bytes[i];
213
219
  const currentConn = base.getConnection(uuid); // Refresh ref
214
220
  if (inSlipFrame) {
215
- if (b === 0xC0) {
221
+ if (b === 0xc0) {
216
222
  if (slipBuffer.length > 0) {
217
223
  const payload = new Uint8Array(slipBuffer);
218
224
  currentConn?.onReceive?.(payload);
@@ -222,14 +228,14 @@ export function SerialMultiDeviceWhisperer({ ...props } = {}) {
222
228
  escapeNext = false;
223
229
  }
224
230
  else if (escapeNext) {
225
- if (b === 0xDC)
226
- slipBuffer.push(0xC0);
227
- else if (b === 0xDD)
228
- slipBuffer.push(0xDB);
231
+ if (b === 0xdc)
232
+ slipBuffer.push(0xc0);
233
+ else if (b === 0xdd)
234
+ slipBuffer.push(0xdb);
229
235
  slipBuffer = []; // reset on error?
230
236
  escapeNext = false;
231
237
  }
232
- else if (b === 0xDB) {
238
+ else if (b === 0xdb) {
233
239
  escapeNext = true;
234
240
  }
235
241
  else {
@@ -237,7 +243,7 @@ export function SerialMultiDeviceWhisperer({ ...props } = {}) {
237
243
  }
238
244
  continue;
239
245
  }
240
- if (b === 0xC0 && currentConn?.slipReadWrite) {
246
+ if (b === 0xc0 && currentConn?.slipReadWrite) {
241
247
  inSlipFrame = true;
242
248
  slipBuffer = [];
243
249
  continue;
@@ -281,18 +287,18 @@ export function SerialMultiDeviceWhisperer({ ...props } = {}) {
281
287
  // Close existing if open
282
288
  if (conn.transport)
283
289
  await disconnect(uuid);
284
- base.updateConnection(uuid, c => ({ ...c, isConnecting: true }));
290
+ base.updateConnection(uuid, (c) => ({ ...c, isConnecting: true }));
285
291
  const transport = new UsbTransport(conn.device);
286
292
  try {
287
293
  await transport.connect(baudrate);
288
294
  // Optional: Auto-restart on connect
289
295
  await restartDevice(uuid, transport);
290
- base.updateConnection(uuid, c => ({
296
+ base.updateConnection(uuid, (c) => ({
291
297
  ...c,
292
298
  transport,
293
299
  baudrate,
294
300
  isConnected: true,
295
- isConnecting: false
301
+ isConnecting: false,
296
302
  }));
297
303
  await conn.onConnect?.();
298
304
  // Start polling loop
@@ -300,7 +306,11 @@ export function SerialMultiDeviceWhisperer({ ...props } = {}) {
300
306
  }
301
307
  catch (err) {
302
308
  console.error(err);
303
- base.updateConnection(uuid, c => ({ ...c, isConnected: false, isConnecting: false }));
309
+ base.updateConnection(uuid, (c) => ({
310
+ ...c,
311
+ isConnected: false,
312
+ isConnecting: false,
313
+ }));
304
314
  await disconnect(uuid);
305
315
  }
306
316
  };
@@ -309,18 +319,18 @@ export function SerialMultiDeviceWhisperer({ ...props } = {}) {
309
319
  if (conn?.transport) {
310
320
  await conn.transport.disconnect();
311
321
  }
312
- base.updateConnection(uuid, c => ({
322
+ base.updateConnection(uuid, (c) => ({
313
323
  ...c,
314
324
  transport: releasePortByDefaultRef.current ? null : c.transport,
315
325
  isConnected: false,
316
- isConnecting: false
326
+ isConnecting: false,
317
327
  }));
318
328
  await conn?.onDisconnect?.();
319
329
  };
320
- const addConnection = async ({ uuid, propCreator }) => {
330
+ const addConnection = async ({ uuid, propCreator, }) => {
321
331
  try {
322
332
  const device = await navigator.usb.requestDevice({
323
- filters: []
333
+ filters: [],
324
334
  });
325
335
  const return_uuid = await base.addConnection({
326
336
  uuid,
@@ -334,9 +344,9 @@ export function SerialMultiDeviceWhisperer({ ...props } = {}) {
334
344
  // Initial connection state
335
345
  ...base.createInitialConnectionState(id),
336
346
  // From props
337
- ...props
347
+ ...props,
338
348
  };
339
- }
349
+ },
340
350
  });
341
351
  const conn = base.getConnection(return_uuid);
342
352
  if (conn?.autoConnect)
@@ -352,7 +362,7 @@ export function SerialMultiDeviceWhisperer({ ...props } = {}) {
352
362
  base.removeConnection(uuid);
353
363
  };
354
364
  const reconnectAll = async (...connectionProps) => {
355
- const connectionIds = base.connections.map(c => c.uuid);
365
+ const connectionIds = base.connections.map((c) => c.uuid);
356
366
  await Promise.all(connectionIds.map(async (id) => {
357
367
  const c = base.getConnection(id);
358
368
  if (!c)
@@ -362,7 +372,9 @@ export function SerialMultiDeviceWhisperer({ ...props } = {}) {
362
372
  return connect(c.uuid, ...connectionProps);
363
373
  }));
364
374
  };
365
- useEffect(() => { base.setIsReady(true); }, []);
375
+ useEffect(() => {
376
+ base.setIsReady(true);
377
+ }, []);
366
378
  return {
367
379
  ...base,
368
380
  addConnection,
@@ -371,6 +383,6 @@ export function SerialMultiDeviceWhisperer({ ...props } = {}) {
371
383
  disconnect,
372
384
  reconnectAll,
373
385
  releasePortByDefault: releasePortByDefaultState,
374
- setReleasePortByDefault: setReleasePortByDefaultState
386
+ setReleasePortByDefault: setReleasePortByDefaultState,
375
387
  };
376
388
  }
@@ -9,11 +9,11 @@ export type DeviceObjectResponse = {
9
9
  deviceConnectedTime: Date | null;
10
10
  deviceLastCommTime: Date | null;
11
11
  };
12
- export declare function WebsocketMultiDeviceWhisperer<AppOrMessageLayer extends WebsocketConnectionState>({ server_url, server_port, ...props }: {
12
+ export declare function useWebsocketMultiDeviceWhisperer<AppOrMessageLayer extends WebsocketConnectionState>({ server_url, server_port, ...props }: {
13
13
  server_url: string;
14
14
  server_port: number;
15
15
  }): {
16
- addConnection: ({ uuid, propCreator }: AddConnectionProps<AppOrMessageLayer>) => Promise<string>;
16
+ addConnection: ({ uuid, propCreator, }: AddConnectionProps<AppOrMessageLayer>) => Promise<string>;
17
17
  removeConnection: (uuid: string) => Promise<void>;
18
18
  connect: (uuid: string, attempt?: number) => Promise<void>;
19
19
  disconnect: (uuid: string) => Promise<void>;