@ecobridge.xyz/devicemanager 3.1.0 → 3.2.0

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 (78) hide show
  1. package/.gitea/workflows/default_tags.yaml +0 -21
  2. package/.smartconfig.json +69 -0
  3. package/changelog.md +38 -1
  4. package/dist_ts/00_commitinfo_data.js +1 -1
  5. package/dist_ts/device/device.classes.device.d.ts +2 -2
  6. package/dist_ts/device/device.classes.device.js +3 -3
  7. package/dist_ts/devicemanager.classes.devicemanager.d.ts +17 -4
  8. package/dist_ts/devicemanager.classes.devicemanager.js +99 -11
  9. package/dist_ts/discovery/discovery.classes.mdns.js +46 -11
  10. package/dist_ts/discovery/discovery.classes.networkscanner.d.ts +5 -1
  11. package/dist_ts/discovery/discovery.classes.networkscanner.js +44 -3
  12. package/dist_ts/discovery/discovery.classes.ssdp.d.ts +52 -21
  13. package/dist_ts/discovery/discovery.classes.ssdp.js +407 -117
  14. package/dist_ts/events/events.observable.d.ts +15 -0
  15. package/dist_ts/events/events.observable.js +22 -0
  16. package/dist_ts/events/index.d.ts +1 -0
  17. package/dist_ts/events/index.js +2 -0
  18. package/dist_ts/factories/index.js +7 -5
  19. package/dist_ts/features/feature.abstract.d.ts +2 -2
  20. package/dist_ts/features/feature.abstract.js +3 -3
  21. package/dist_ts/features/feature.print.d.ts +3 -0
  22. package/dist_ts/features/feature.print.js +17 -2
  23. package/dist_ts/features/feature.scan.d.ts +5 -2
  24. package/dist_ts/features/feature.scan.js +98 -27
  25. package/dist_ts/index.d.ts +4 -2
  26. package/dist_ts/index.js +4 -2
  27. package/dist_ts/interfaces/feature.interfaces.d.ts +22 -2
  28. package/dist_ts/interfaces/index.d.ts +22 -4
  29. package/dist_ts/interfaces/index.js +1 -1
  30. package/dist_ts/plugins.d.ts +6 -15
  31. package/dist_ts/plugins.js +7 -19
  32. package/dist_ts/protocols/index.d.ts +2 -1
  33. package/dist_ts/protocols/index.js +4 -2
  34. package/dist_ts/protocols/protocol.brother.d.ts +67 -0
  35. package/dist_ts/protocols/protocol.brother.js +560 -0
  36. package/dist_ts/protocols/protocol.escl.d.ts +13 -4
  37. package/dist_ts/protocols/protocol.escl.js +345 -178
  38. package/dist_ts/protocols/protocol.ipp.d.ts +2 -0
  39. package/dist_ts/protocols/protocol.ipp.js +22 -2
  40. package/dist_ts/providers/index.d.ts +1 -0
  41. package/dist_ts/providers/index.js +2 -0
  42. package/dist_ts/providers/provider.smb.d.ts +60 -0
  43. package/dist_ts/providers/provider.smb.js +224 -0
  44. package/license.md +21 -0
  45. package/package.json +15 -20
  46. package/pnpm-workspace.yaml +6 -0
  47. package/readme.md +157 -78
  48. package/test/test.brother.node.ts +306 -0
  49. package/test/test.escl.node.ts +233 -0
  50. package/test/test.ipp.node.ts +75 -0
  51. package/test/{test.ts → test.node.ts} +37 -45
  52. package/test/test.smb.node.ts +123 -0
  53. package/test/test.ssdp.node.ts +978 -0
  54. package/ts/00_commitinfo_data.ts +1 -1
  55. package/ts/device/device.classes.device.ts +2 -2
  56. package/ts/devicemanager.classes.devicemanager.ts +125 -12
  57. package/ts/discovery/discovery.classes.mdns.ts +40 -11
  58. package/ts/discovery/discovery.classes.networkscanner.ts +51 -2
  59. package/ts/discovery/discovery.classes.ssdp.ts +493 -130
  60. package/ts/events/events.observable.ts +31 -0
  61. package/ts/events/index.ts +4 -0
  62. package/ts/factories/index.ts +7 -5
  63. package/ts/features/feature.abstract.ts +2 -2
  64. package/ts/features/feature.print.ts +16 -1
  65. package/ts/features/feature.scan.ts +93 -27
  66. package/ts/index.ts +21 -0
  67. package/ts/interfaces/feature.interfaces.ts +23 -2
  68. package/ts/interfaces/index.ts +23 -4
  69. package/ts/plugins.ts +6 -25
  70. package/ts/protocols/index.ts +11 -1
  71. package/ts/protocols/protocol.brother.ts +648 -0
  72. package/ts/protocols/protocol.escl.ts +348 -191
  73. package/ts/protocols/protocol.ipp.ts +24 -1
  74. package/ts/providers/index.ts +7 -0
  75. package/ts/providers/provider.smb.ts +326 -0
  76. package/dist_ts/protocols/protocol.ipp.old.d.ts +0 -45
  77. package/dist_ts/protocols/protocol.ipp.old.js +0 -284
  78. package/npmextra.json +0 -24
@@ -1,4 +1,10 @@
1
1
  import * as plugins from '../plugins.js';
2
+ const SSDP_ADDRESS = '239.255.255.250';
3
+ const SSDP_PORT = 1900;
4
+ const SSDP_MULTICAST_TTL = 4;
5
+ const SEARCH_INTERVAL_MS = 30000;
6
+ const DESCRIPTION_TIMEOUT_MS = 5000;
7
+ const SOAP_TIMEOUT_MS = 10000;
2
8
  /**
3
9
  * SSDP service types for device discovery
4
10
  */
@@ -14,160 +20,380 @@ export const SSDP_SERVICE_TYPES = {
14
20
  BASIC_DEVICE: 'urn:schemas-upnp-org:device:Basic:1',
15
21
  INTERNET_GATEWAY: 'urn:schemas-upnp-org:device:InternetGatewayDevice:1',
16
22
  };
23
+ const DEFAULT_SERVICE_TYPES = [
24
+ SSDP_SERVICE_TYPES.ROOT,
25
+ SSDP_SERVICE_TYPES.MEDIA_RENDERER,
26
+ SSDP_SERVICE_TYPES.MEDIA_SERVER,
27
+ SSDP_SERVICE_TYPES.SONOS_ZONE_PLAYER,
28
+ ];
17
29
  /**
18
- * SSDP Discovery service using node-ssdp
30
+ * Focused SSDP discovery client backed by native Node.js UDP sockets.
19
31
  */
20
32
  export class SsdpDiscovery extends plugins.events.EventEmitter {
21
- client = null;
33
+ dependencies;
22
34
  devices = new Map();
23
- running = false;
35
+ foundUsns = new Set();
36
+ pendingDescriptions = new Map();
37
+ sockets = [];
38
+ lifecycleState = 'stopped';
39
+ lifecycleTail = Promise.resolve();
40
+ generation = 0;
41
+ startupError = null;
24
42
  searchInterval = null;
25
- constructor() {
43
+ serviceTypes = [...DEFAULT_SERVICE_TYPES];
44
+ constructor(dependencies = {}) {
26
45
  super();
46
+ this.dependencies = {
47
+ createSocket: dependencies.createSocket ?? (() => plugins.dgram.createSocket({
48
+ type: 'udp4',
49
+ reuseAddr: true,
50
+ })),
51
+ networkInterfaces: dependencies.networkInterfaces ?? plugins.os.networkInterfaces,
52
+ fetch: dependencies.fetch ?? globalThis.fetch,
53
+ setInterval: dependencies.setInterval ?? globalThis.setInterval,
54
+ clearInterval: dependencies.clearInterval ?? globalThis.clearInterval,
55
+ setTimeout: dependencies.setTimeout ?? globalThis.setTimeout,
56
+ clearTimeout: dependencies.clearTimeout ?? globalThis.clearTimeout,
57
+ };
27
58
  }
28
59
  /**
29
- * Start SSDP discovery
60
+ * Start SSDP discovery.
30
61
  */
31
62
  async start(serviceTypes) {
32
- if (this.running) {
33
- return;
63
+ const typesToSearch = [...(serviceTypes ?? DEFAULT_SERVICE_TYPES)];
64
+ for (const serviceType of typesToSearch) {
65
+ this.validateServiceType(serviceType);
34
66
  }
35
- this.running = true;
36
- this.client = new plugins.nodeSsdp.Client();
37
- // Handle SSDP responses
38
- this.client.on('response', (headers, statusCode, rinfo) => {
39
- this.handleSsdpResponse(headers, rinfo);
40
- });
41
- // Search for devices
42
- const typesToSearch = serviceTypes ?? [
43
- SSDP_SERVICE_TYPES.ROOT,
44
- SSDP_SERVICE_TYPES.MEDIA_RENDERER,
45
- SSDP_SERVICE_TYPES.MEDIA_SERVER,
46
- SSDP_SERVICE_TYPES.SONOS_ZONE_PLAYER,
47
- ];
48
- // Initial search
49
- for (const st of typesToSearch) {
50
- this.client.search(st);
51
- }
52
- // Periodic re-search (every 30 seconds)
53
- this.searchInterval = setInterval(() => {
54
- if (this.client) {
55
- for (const st of typesToSearch) {
56
- this.client.search(st);
57
- }
67
+ return this.queueLifecycle(async () => {
68
+ if (this.lifecycleState === 'running') {
69
+ return;
58
70
  }
59
- }, 30000);
60
- this.emit('started');
71
+ await this.startInternal(typesToSearch);
72
+ });
61
73
  }
62
74
  /**
63
- * Stop SSDP discovery
75
+ * Stop SSDP discovery.
64
76
  */
65
77
  async stop() {
66
- if (!this.running) {
67
- return;
68
- }
69
- this.running = false;
70
- if (this.searchInterval) {
71
- clearInterval(this.searchInterval);
72
- this.searchInterval = null;
73
- }
74
- if (this.client) {
75
- this.client.stop();
76
- this.client = null;
77
- }
78
- this.emit('stopped');
78
+ return this.queueLifecycle(async () => {
79
+ if (this.lifecycleState === 'stopped') {
80
+ return;
81
+ }
82
+ const shouldEmitStopped = this.lifecycleState === 'running';
83
+ await this.cleanupResources();
84
+ if (shouldEmitStopped) {
85
+ this.emit('stopped');
86
+ }
87
+ });
79
88
  }
80
89
  /**
81
- * Check if discovery is running
90
+ * Check if discovery is running.
82
91
  */
83
92
  get isRunning() {
84
- return this.running;
93
+ return this.lifecycleState === 'running';
85
94
  }
86
95
  /**
87
- * Get all discovered devices
96
+ * Get all discovered devices.
88
97
  */
89
98
  getDevices() {
90
99
  return Array.from(this.devices.values());
91
100
  }
92
101
  /**
93
- * Get devices by service type
102
+ * Get devices by service type.
94
103
  */
95
104
  getDevicesByType(serviceType) {
96
- return this.getDevices().filter((d) => d.serviceType === serviceType);
105
+ return this.getDevices().filter((device) => device.serviceType === serviceType);
97
106
  }
98
107
  /**
99
- * Search for a specific service type
108
+ * Search for a specific service type.
100
109
  */
101
110
  search(serviceType) {
102
- if (this.client && this.running) {
103
- this.client.search(serviceType);
111
+ this.validateServiceType(serviceType);
112
+ if (this.lifecycleState !== 'running') {
113
+ return;
104
114
  }
115
+ const activeGeneration = this.generation;
116
+ void this.sendSearches([serviceType], activeGeneration).catch((error) => {
117
+ this.handleRuntimeError(this.toError(error), activeGeneration);
118
+ });
105
119
  }
106
- /**
107
- * Handle SSDP response
108
- */
109
- handleSsdpResponse(headers, rinfo) {
110
- const usn = headers['USN'] || headers['usn'];
111
- const location = headers['LOCATION'] || headers['location'];
112
- const st = headers['ST'] || headers['st'];
113
- if (!usn || !location) {
120
+ queueLifecycle(operation) {
121
+ const result = this.lifecycleTail.then(operation);
122
+ this.lifecycleTail = result.catch(() => { });
123
+ return result;
124
+ }
125
+ async startInternal(typesToSearch) {
126
+ this.lifecycleState = 'starting';
127
+ this.startupError = null;
128
+ const activeGeneration = ++this.generation;
129
+ try {
130
+ const addresses = this.getInterfaceAddresses();
131
+ if (addresses.length === 0) {
132
+ throw new Error('No non-internal IPv4 interfaces available for SSDP discovery.');
133
+ }
134
+ for (const address of addresses) {
135
+ const resource = this.createSocketResource(address, activeGeneration);
136
+ this.sockets.push(resource);
137
+ await this.bindSocket(resource);
138
+ }
139
+ await this.sendSearches(typesToSearch, activeGeneration);
140
+ if (this.startupError) {
141
+ throw this.startupError;
142
+ }
143
+ this.serviceTypes = typesToSearch;
144
+ this.lifecycleState = 'running';
145
+ this.searchInterval = this.dependencies.setInterval(() => {
146
+ const intervalGeneration = this.generation;
147
+ void this.sendSearches(this.serviceTypes, intervalGeneration).catch((error) => {
148
+ this.handleRuntimeError(this.toError(error), intervalGeneration);
149
+ });
150
+ }, SEARCH_INTERVAL_MS);
151
+ this.unrefTimer(this.searchInterval);
152
+ this.emit('started');
153
+ }
154
+ catch (error) {
155
+ await this.cleanupResources();
156
+ throw error;
157
+ }
158
+ }
159
+ getInterfaceAddresses() {
160
+ const addresses = new Set();
161
+ const interfaces = this.dependencies.networkInterfaces();
162
+ for (const [, interfaceInfos] of Object.entries(interfaces).sort(([a], [b]) => a.localeCompare(b))) {
163
+ for (const interfaceInfo of interfaceInfos ?? []) {
164
+ if (!interfaceInfo.internal && interfaceInfo.family === 'IPv4') {
165
+ addresses.add(interfaceInfo.address);
166
+ }
167
+ }
168
+ }
169
+ return Array.from(addresses).sort((a, b) => a.localeCompare(b));
170
+ }
171
+ createSocketResource(address, activeGeneration) {
172
+ const socket = this.dependencies.createSocket();
173
+ const onMessage = (message, rinfo) => {
174
+ if (this.lifecycleState === 'running' && this.generation === activeGeneration) {
175
+ this.handleSsdpMessage(message, rinfo, activeGeneration);
176
+ }
177
+ };
178
+ const onError = (error) => {
179
+ if (this.lifecycleState === 'starting' && this.generation === activeGeneration) {
180
+ this.startupError ??= error;
181
+ return;
182
+ }
183
+ this.handleRuntimeError(error, activeGeneration);
184
+ };
185
+ socket.on('message', onMessage);
186
+ socket.on('error', onError);
187
+ return { address, socket, onMessage, onError };
188
+ }
189
+ async bindSocket(resource) {
190
+ const { address, socket } = resource;
191
+ await new Promise((resolve, reject) => {
192
+ const cleanupBindListeners = () => {
193
+ socket.off('listening', onListening);
194
+ socket.off('error', onBindError);
195
+ };
196
+ const onListening = () => {
197
+ cleanupBindListeners();
198
+ resolve();
199
+ };
200
+ const onBindError = (error) => {
201
+ cleanupBindListeners();
202
+ reject(error);
203
+ };
204
+ socket.once('listening', onListening);
205
+ socket.once('error', onBindError);
206
+ try {
207
+ socket.bind({ port: 0, address, exclusive: true });
208
+ }
209
+ catch (error) {
210
+ cleanupBindListeners();
211
+ reject(error);
212
+ }
213
+ });
214
+ if (this.startupError) {
215
+ throw this.startupError;
216
+ }
217
+ socket.setMulticastInterface(address);
218
+ socket.setMulticastTTL(SSDP_MULTICAST_TTL);
219
+ // M-SEARCH responses are unicast to this bound socket, so membership is not needed.
220
+ socket.unref();
221
+ }
222
+ async sendSearches(serviceTypes, activeGeneration) {
223
+ if (this.generation !== activeGeneration ||
224
+ (this.lifecycleState !== 'starting' && this.lifecycleState !== 'running')) {
114
225
  return;
115
226
  }
116
- // Parse location URL
117
- let address = rinfo.address;
118
- let port = 80;
119
- try {
120
- const url = new URL(location);
121
- address = url.hostname;
122
- port = parseInt(url.port) || (url.protocol === 'https:' ? 443 : 80);
227
+ const sends = [];
228
+ for (const serviceType of serviceTypes) {
229
+ const packet = this.createSearchPacket(serviceType);
230
+ for (const resource of this.sockets) {
231
+ sends.push(this.sendPacket(resource.socket, packet));
232
+ }
123
233
  }
124
- catch {
125
- // Keep rinfo address
234
+ const results = await Promise.allSettled(sends);
235
+ const failure = results.find((result) => result.status === 'rejected');
236
+ if (failure) {
237
+ throw failure.reason;
238
+ }
239
+ }
240
+ createSearchPacket(serviceType) {
241
+ return Buffer.from([
242
+ 'M-SEARCH * HTTP/1.1',
243
+ `HOST: ${SSDP_ADDRESS}:${SSDP_PORT}`,
244
+ 'MAN: "ssdp:discover"',
245
+ 'MX: 3',
246
+ `ST: ${serviceType}`,
247
+ '',
248
+ '',
249
+ ].join('\r\n'), 'ascii');
250
+ }
251
+ async sendPacket(socket, packet) {
252
+ await new Promise((resolve, reject) => {
253
+ try {
254
+ socket.send(packet, SSDP_PORT, SSDP_ADDRESS, (error) => {
255
+ if (error) {
256
+ reject(error);
257
+ }
258
+ else {
259
+ resolve();
260
+ }
261
+ });
262
+ }
263
+ catch (error) {
264
+ reject(error);
265
+ }
266
+ });
267
+ }
268
+ handleRuntimeError(error, activeGeneration) {
269
+ if (this.lifecycleState !== 'running' || this.generation !== activeGeneration) {
270
+ return;
271
+ }
272
+ void this.stop();
273
+ if (this.listenerCount('error') > 0) {
274
+ this.emit('error', error);
275
+ }
276
+ }
277
+ handleSsdpMessage(message, rinfo, activeGeneration) {
278
+ const response = this.parseSsdpResponse(message);
279
+ if (!response || response.statusCode !== 200) {
280
+ return;
281
+ }
282
+ this.handleSsdpResponse(response.headers, rinfo, activeGeneration);
283
+ }
284
+ parseSsdpResponse(message) {
285
+ const lines = message.toString('utf8').split(/\r?\n/);
286
+ const statusLine = lines.shift()?.trim() ?? '';
287
+ const statusMatch = statusLine.match(/^HTTP\/\d\.\d\s+(\d{3})(?:\s|$)/i);
288
+ if (!statusMatch) {
289
+ return null;
290
+ }
291
+ const headers = {};
292
+ for (const line of lines) {
293
+ if (line.trim() === '') {
294
+ break;
295
+ }
296
+ const separatorIndex = line.indexOf(':');
297
+ if (separatorIndex <= 0) {
298
+ continue;
299
+ }
300
+ const name = line.slice(0, separatorIndex).trim().toUpperCase();
301
+ if (name) {
302
+ headers[name] = line.slice(separatorIndex + 1).trim();
303
+ }
304
+ }
305
+ return {
306
+ statusCode: Number.parseInt(statusMatch[1], 10),
307
+ headers,
308
+ };
309
+ }
310
+ handleSsdpResponse(headers, rinfo, activeGeneration) {
311
+ const usn = headers.USN;
312
+ const location = headers.LOCATION;
313
+ if (!usn || !location) {
314
+ return;
315
+ }
316
+ const locationInfo = this.parseLocation(location, rinfo.address);
317
+ const existing = this.devices.get(usn);
318
+ if (existing) {
319
+ existing.serviceType = headers.ST || 'unknown';
320
+ existing.location = location;
321
+ existing.address = locationInfo.address;
322
+ existing.port = locationInfo.port;
323
+ existing.headers = { ...headers };
324
+ if (this.foundUsns.has(usn)) {
325
+ this.emit('device:updated', existing);
326
+ }
327
+ return;
126
328
  }
127
329
  const device = {
128
330
  usn,
129
- serviceType: st || 'unknown',
331
+ serviceType: headers.ST || 'unknown',
130
332
  location,
131
- address,
132
- port,
333
+ address: locationInfo.address,
334
+ port: locationInfo.port,
133
335
  headers: { ...headers },
134
336
  };
135
- const isNew = !this.devices.has(usn);
136
337
  this.devices.set(usn, device);
137
- if (isNew) {
138
- // Fetch device description
139
- this.fetchDeviceDescription(device).then(() => {
140
- this.emit('device:found', device);
141
- }).catch(() => {
142
- // Still emit even without description
143
- this.emit('device:found', device);
144
- });
338
+ this.startDescriptionFetch(device, activeGeneration);
339
+ }
340
+ parseLocation(location, fallbackAddress) {
341
+ try {
342
+ const url = new URL(location);
343
+ return {
344
+ address: url.hostname,
345
+ port: Number.parseInt(url.port, 10) || (url.protocol === 'https:' ? 443 : 80),
346
+ };
145
347
  }
146
- else {
147
- this.emit('device:updated', device);
348
+ catch {
349
+ return { address: fallbackAddress, port: 80 };
148
350
  }
149
351
  }
150
- /**
151
- * Fetch and parse device description XML
152
- */
153
- async fetchDeviceDescription(device) {
352
+ startDescriptionFetch(device, activeGeneration) {
353
+ const abortController = new AbortController();
354
+ const timeout = this.dependencies.setTimeout(() => {
355
+ abortController.abort();
356
+ }, DESCRIPTION_TIMEOUT_MS);
357
+ this.unrefTimer(timeout);
358
+ const pending = {
359
+ device,
360
+ abortController,
361
+ timeout,
362
+ promise: Promise.resolve(),
363
+ };
364
+ pending.promise = (async () => {
365
+ await this.fetchDeviceDescription(device, abortController.signal);
366
+ if (this.lifecycleState === 'running' &&
367
+ this.generation === activeGeneration &&
368
+ this.devices.get(device.usn) === device &&
369
+ !this.foundUsns.has(device.usn)) {
370
+ this.foundUsns.add(device.usn);
371
+ this.emit('device:found', device);
372
+ }
373
+ })().finally(() => {
374
+ this.dependencies.clearTimeout(timeout);
375
+ if (this.pendingDescriptions.get(device.usn) === pending) {
376
+ this.pendingDescriptions.delete(device.usn);
377
+ }
378
+ });
379
+ this.pendingDescriptions.set(device.usn, pending);
380
+ }
381
+ async fetchDeviceDescription(device, signal) {
154
382
  try {
155
- const response = await fetch(device.location, {
156
- signal: AbortSignal.timeout(5000),
157
- });
383
+ const response = await this.dependencies.fetch(device.location, { signal });
158
384
  if (!response.ok) {
385
+ await this.cancelResponseBody(response);
159
386
  return;
160
387
  }
161
388
  const xml = await response.text();
162
- device.description = this.parseDeviceDescription(xml);
389
+ if (!signal.aborted) {
390
+ device.description = this.parseDeviceDescription(xml);
391
+ }
163
392
  }
164
393
  catch {
165
- // Ignore fetch errors
394
+ // Devices without a reachable description are still valid SSDP results.
166
395
  }
167
396
  }
168
- /**
169
- * Parse UPnP device description XML
170
- */
171
397
  parseDeviceDescription(xml) {
172
398
  const getTagContent = (tag, source = xml) => {
173
399
  const regex = new RegExp(`<${tag}[^>]*>([^<]*)</${tag}>`, 'i');
@@ -179,7 +405,6 @@ export class SsdpDiscovery extends plugins.events.EventEmitter {
179
405
  const match = source.match(regex);
180
406
  return match?.[1] ?? '';
181
407
  };
182
- // Parse services
183
408
  const services = [];
184
409
  const serviceListBlock = getTagBlock('serviceList');
185
410
  const serviceMatches = serviceListBlock.match(/<service>[\s\S]*?<\/service>/gi) || [];
@@ -192,16 +417,15 @@ export class SsdpDiscovery extends plugins.events.EventEmitter {
192
417
  eventSubURL: getTagContent('eventSubURL', serviceXml),
193
418
  });
194
419
  }
195
- // Parse icons
196
420
  const icons = [];
197
421
  const iconListBlock = getTagBlock('iconList');
198
422
  const iconMatches = iconListBlock.match(/<icon>[\s\S]*?<\/icon>/gi) || [];
199
423
  for (const iconXml of iconMatches) {
200
424
  icons.push({
201
425
  mimetype: getTagContent('mimetype', iconXml),
202
- width: parseInt(getTagContent('width', iconXml)) || 0,
203
- height: parseInt(getTagContent('height', iconXml)) || 0,
204
- depth: parseInt(getTagContent('depth', iconXml)) || 0,
426
+ width: Number.parseInt(getTagContent('width', iconXml), 10) || 0,
427
+ height: Number.parseInt(getTagContent('height', iconXml), 10) || 0,
428
+ depth: Number.parseInt(getTagContent('depth', iconXml), 10) || 0,
205
429
  url: getTagContent('url', iconXml),
206
430
  });
207
431
  }
@@ -220,11 +444,66 @@ export class SsdpDiscovery extends plugins.events.EventEmitter {
220
444
  icons: icons.length > 0 ? icons : undefined,
221
445
  };
222
446
  }
447
+ async cleanupResources() {
448
+ this.lifecycleState = 'stopping';
449
+ ++this.generation;
450
+ if (this.searchInterval) {
451
+ this.dependencies.clearInterval(this.searchInterval);
452
+ this.searchInterval = null;
453
+ }
454
+ const pendingDescriptions = Array.from(this.pendingDescriptions.values());
455
+ for (const pending of pendingDescriptions) {
456
+ this.dependencies.clearTimeout(pending.timeout);
457
+ pending.abortController.abort();
458
+ }
459
+ await Promise.allSettled(pendingDescriptions.map((pending) => pending.promise));
460
+ for (const pending of pendingDescriptions) {
461
+ if (!this.foundUsns.has(pending.device.usn) && this.devices.get(pending.device.usn) === pending.device) {
462
+ this.devices.delete(pending.device.usn);
463
+ }
464
+ }
465
+ this.pendingDescriptions.clear();
466
+ const sockets = this.sockets;
467
+ this.sockets = [];
468
+ await Promise.all(sockets.map(async (resource) => {
469
+ resource.socket.off('message', resource.onMessage);
470
+ resource.socket.off('error', resource.onError);
471
+ await new Promise((resolve) => {
472
+ try {
473
+ resource.socket.close(resolve);
474
+ }
475
+ catch {
476
+ resolve();
477
+ }
478
+ });
479
+ }));
480
+ this.startupError = null;
481
+ this.lifecycleState = 'stopped';
482
+ }
483
+ validateServiceType(serviceType) {
484
+ if (!serviceType || /[\r\n]/.test(serviceType)) {
485
+ throw new Error('SSDP service type must be non-empty and cannot contain line breaks.');
486
+ }
487
+ }
488
+ unrefTimer(timer) {
489
+ const unref = timer.unref;
490
+ unref?.call(timer);
491
+ }
492
+ toError(error) {
493
+ return error instanceof Error ? error : new Error(String(error));
494
+ }
495
+ async cancelResponseBody(response) {
496
+ try {
497
+ await response.body?.cancel();
498
+ }
499
+ catch {
500
+ // The fetch timeout/abort remains the final cleanup path for a failed body.
501
+ }
502
+ }
223
503
  /**
224
- * Make a UPnP SOAP request
504
+ * Make a UPnP SOAP request.
225
505
  */
226
506
  async soapRequest(controlUrl, serviceType, action, args = {}) {
227
- // Build SOAP body
228
507
  let argsXml = '';
229
508
  for (const [key, value] of Object.entries(args)) {
230
509
  argsXml += `<${key}>${value}</${key}>`;
@@ -237,19 +516,30 @@ export class SsdpDiscovery extends plugins.events.EventEmitter {
237
516
  </u:${action}>
238
517
  </s:Body>
239
518
  </s:Envelope>`;
240
- const response = await fetch(controlUrl, {
241
- method: 'POST',
242
- headers: {
243
- 'Content-Type': 'text/xml; charset=utf-8',
244
- 'SOAPACTION': `"${serviceType}#${action}"`,
245
- },
246
- body: soapBody,
247
- signal: AbortSignal.timeout(10000),
248
- });
249
- if (!response.ok) {
250
- throw new Error(`SOAP request failed: ${response.status}`);
519
+ const abortController = new AbortController();
520
+ const timeout = this.dependencies.setTimeout(() => {
521
+ abortController.abort();
522
+ }, SOAP_TIMEOUT_MS);
523
+ this.unrefTimer(timeout);
524
+ try {
525
+ const response = await this.dependencies.fetch(controlUrl, {
526
+ method: 'POST',
527
+ headers: {
528
+ 'Content-Type': 'text/xml; charset=utf-8',
529
+ 'SOAPACTION': `"${serviceType}#${action}"`,
530
+ },
531
+ body: soapBody,
532
+ signal: abortController.signal,
533
+ });
534
+ if (!response.ok) {
535
+ await this.cancelResponseBody(response);
536
+ throw new Error(`SOAP request failed: ${response.status}`);
537
+ }
538
+ return await response.text();
539
+ }
540
+ finally {
541
+ this.dependencies.clearTimeout(timeout);
251
542
  }
252
- return response.text();
253
543
  }
254
544
  }
255
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGlzY292ZXJ5LmNsYXNzZXMuc3NkcC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL2Rpc2NvdmVyeS9kaXNjb3ZlcnkuY2xhc3Nlcy5zc2RwLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sZUFBZSxDQUFDO0FBRXpDOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sa0JBQWtCLEdBQUc7SUFDaEMsY0FBYztJQUNkLElBQUksRUFBRSxpQkFBaUI7SUFFdkIsZUFBZTtJQUNmLGNBQWMsRUFBRSw2Q0FBNkM7SUFDN0QsWUFBWSxFQUFFLDJDQUEyQztJQUV6RCxRQUFRO0lBQ1IsaUJBQWlCLEVBQUUsMENBQTBDO0lBRTdELFVBQVU7SUFDVixZQUFZLEVBQUUscUNBQXFDO0lBQ25ELGdCQUFnQixFQUFFLHFEQUFxRDtDQUN4RSxDQUFDO0FBd0RGOztHQUVHO0FBQ0gsTUFBTSxPQUFPLGFBQWMsU0FBUSxPQUFPLENBQUMsTUFBTSxDQUFDLFlBQVk7SUFDcEQsTUFBTSxHQUF3RCxJQUFJLENBQUM7SUFDbkUsT0FBTyxHQUE2QixJQUFJLEdBQUcsRUFBRSxDQUFDO0lBQzlDLE9BQU8sR0FBRyxLQUFLLENBQUM7SUFDaEIsY0FBYyxHQUEwQixJQUFJLENBQUM7SUFFckQ7UUFDRSxLQUFLLEVBQUUsQ0FBQztJQUNWLENBQUM7SUFFRDs7T0FFRztJQUNJLEtBQUssQ0FBQyxLQUFLLENBQUMsWUFBdUI7UUFDeEMsSUFBSSxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDakIsT0FBTztRQUNULENBQUM7UUFFRCxJQUFJLENBQUMsT0FBTyxHQUFHLElBQUksQ0FBQztRQUNwQixJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUU1Qyx3QkFBd0I7UUFDeEIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsVUFBVSxFQUFFLENBQUMsT0FB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
545
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGlzY292ZXJ5LmNsYXNzZXMuc3NkcC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL2Rpc2NvdmVyeS9kaXNjb3ZlcnkuY2xhc3Nlcy5zc2RwLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sZUFBZSxDQUFDO0FBRXpDLE1BQU0sWUFBWSxHQUFHLGlCQUFpQixDQUFDO0FBQ3ZDLE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQztBQUN2QixNQUFNLGtCQUFrQixHQUFHLENBQUMsQ0FBQztBQUM3QixNQUFNLGtCQUFrQixHQUFHLEtBQUssQ0FBQztBQUNqQyxNQUFNLHNCQUFzQixHQUFHLElBQUksQ0FBQztBQUNwQyxNQUFNLGVBQWUsR0FBRyxLQUFLLENBQUM7QUFLOUI7O0dBRUc7QUFDSCxNQUFNLENBQUMsTUFBTSxrQkFBa0IsR0FBRztJQUNoQyxjQUFjO0lBQ2QsSUFBSSxFQUFFLGlCQUFpQjtJQUV2QixlQUFlO0lBQ2YsY0FBYyxFQUFFLDZDQUE2QztJQUM3RCxZQUFZLEVBQUUsMkNBQTJDO0lBRXpELFFBQVE7SUFDUixpQkFBaUIsRUFBRSwwQ0FBMEM7SUFFN0QsVUFBVTtJQUNWLFlBQVksRUFBRSxxQ0FBcUM7SUFDbkQsZ0JBQWdCLEVBQUUscURBQXFEO0NBQ3hFLENBQUM7QUFFRixNQUFNLHFCQUFxQixHQUFHO0lBQzVCLGtCQUFrQixDQUFDLElBQUk7SUFDdkIsa0JBQWtCLENBQUMsY0FBYztJQUNqQyxrQkFBa0IsQ0FBQyxZQUFZO0lBQy9CLGtCQUFrQixDQUFDLGlCQUFpQjtDQUNyQyxDQUFDO0FBeUZGOztHQUVHO0FBQ0gsTUFBTSxPQUFPLGFBQWMsU0FBUSxPQUFPLENBQUMsTUFBTSxDQUFDLFlBQVk7SUFDM0MsWUFBWSxDQUF1QztJQUNuRCxPQUFPLEdBQUcsSUFBSSxHQUFHLEVBQXVCLENBQUM7SUFDekMsU0FBUyxHQUFHLElBQUksR0FBRyxFQUFVLENBQUM7SUFDOUIsbUJB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