@ecobridge.xyz/devicemanager 3.0.2

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 (137) hide show
  1. package/.gitea/workflows/default_nottags.yaml +66 -0
  2. package/.gitea/workflows/default_tags.yaml +124 -0
  3. package/.vscode/launch.json +11 -0
  4. package/.vscode/settings.json +26 -0
  5. package/changelog.md +91 -0
  6. package/dist_ts/00_commitinfo_data.d.ts +8 -0
  7. package/dist_ts/00_commitinfo_data.js +9 -0
  8. package/dist_ts/device/device.classes.device.d.ts +205 -0
  9. package/dist_ts/device/device.classes.device.js +344 -0
  10. package/dist_ts/devicemanager.classes.devicemanager.d.ts +210 -0
  11. package/dist_ts/devicemanager.classes.devicemanager.js +835 -0
  12. package/dist_ts/discovery/discovery.classes.homeassistant.d.ts +101 -0
  13. package/dist_ts/discovery/discovery.classes.homeassistant.js +313 -0
  14. package/dist_ts/discovery/discovery.classes.mdns.d.ts +90 -0
  15. package/dist_ts/discovery/discovery.classes.mdns.js +291 -0
  16. package/dist_ts/discovery/discovery.classes.networkscanner.d.ts +64 -0
  17. package/dist_ts/discovery/discovery.classes.networkscanner.js +535 -0
  18. package/dist_ts/discovery/discovery.classes.ssdp.d.ts +112 -0
  19. package/dist_ts/discovery/discovery.classes.ssdp.js +255 -0
  20. package/dist_ts/factories/index.d.ts +190 -0
  21. package/dist_ts/factories/index.js +350 -0
  22. package/dist_ts/features/feature.abstract.d.ts +118 -0
  23. package/dist_ts/features/feature.abstract.js +172 -0
  24. package/dist_ts/features/feature.camera.d.ts +95 -0
  25. package/dist_ts/features/feature.camera.js +162 -0
  26. package/dist_ts/features/feature.climate.d.ts +174 -0
  27. package/dist_ts/features/feature.climate.js +328 -0
  28. package/dist_ts/features/feature.cover.d.ts +121 -0
  29. package/dist_ts/features/feature.cover.js +217 -0
  30. package/dist_ts/features/feature.fan.d.ts +127 -0
  31. package/dist_ts/features/feature.fan.js +235 -0
  32. package/dist_ts/features/feature.light.d.ts +156 -0
  33. package/dist_ts/features/feature.light.js +296 -0
  34. package/dist_ts/features/feature.lock.d.ts +103 -0
  35. package/dist_ts/features/feature.lock.js +170 -0
  36. package/dist_ts/features/feature.playback.d.ts +98 -0
  37. package/dist_ts/features/feature.playback.js +191 -0
  38. package/dist_ts/features/feature.power.d.ts +79 -0
  39. package/dist_ts/features/feature.power.js +173 -0
  40. package/dist_ts/features/feature.print.d.ts +78 -0
  41. package/dist_ts/features/feature.print.js +226 -0
  42. package/dist_ts/features/feature.scan.d.ts +79 -0
  43. package/dist_ts/features/feature.scan.js +315 -0
  44. package/dist_ts/features/feature.sensor.d.ts +95 -0
  45. package/dist_ts/features/feature.sensor.js +147 -0
  46. package/dist_ts/features/feature.snmp.d.ts +88 -0
  47. package/dist_ts/features/feature.snmp.js +175 -0
  48. package/dist_ts/features/feature.switch.d.ts +75 -0
  49. package/dist_ts/features/feature.switch.js +126 -0
  50. package/dist_ts/features/feature.volume.d.ts +104 -0
  51. package/dist_ts/features/feature.volume.js +186 -0
  52. package/dist_ts/features/index.d.ts +19 -0
  53. package/dist_ts/features/index.js +23 -0
  54. package/dist_ts/helpers/helpers.iprange.d.ts +35 -0
  55. package/dist_ts/helpers/helpers.iprange.js +151 -0
  56. package/dist_ts/helpers/helpers.retry.d.ts +25 -0
  57. package/dist_ts/helpers/helpers.retry.js +69 -0
  58. package/dist_ts/index.d.ts +15 -0
  59. package/dist_ts/index.js +40 -0
  60. package/dist_ts/interfaces/feature.interfaces.d.ts +255 -0
  61. package/dist_ts/interfaces/feature.interfaces.js +6 -0
  62. package/dist_ts/interfaces/homeassistant.interfaces.d.ts +550 -0
  63. package/dist_ts/interfaces/homeassistant.interfaces.js +111 -0
  64. package/dist_ts/interfaces/index.d.ts +330 -0
  65. package/dist_ts/interfaces/index.js +16 -0
  66. package/dist_ts/interfaces/smarthome.interfaces.d.ts +282 -0
  67. package/dist_ts/interfaces/smarthome.interfaces.js +7 -0
  68. package/dist_ts/paths.d.ts +1 -0
  69. package/dist_ts/paths.js +3 -0
  70. package/dist_ts/plugins.d.ts +23 -0
  71. package/dist_ts/plugins.js +30 -0
  72. package/dist_ts/protocols/index.d.ts +12 -0
  73. package/dist_ts/protocols/index.js +21 -0
  74. package/dist_ts/protocols/protocol.escl.d.ts +58 -0
  75. package/dist_ts/protocols/protocol.escl.js +364 -0
  76. package/dist_ts/protocols/protocol.homeassistant.d.ts +248 -0
  77. package/dist_ts/protocols/protocol.homeassistant.js +611 -0
  78. package/dist_ts/protocols/protocol.ipp.d.ts +45 -0
  79. package/dist_ts/protocols/protocol.ipp.js +284 -0
  80. package/dist_ts/protocols/protocol.nut.d.ts +160 -0
  81. package/dist_ts/protocols/protocol.nut.js +364 -0
  82. package/dist_ts/protocols/protocol.sane.d.ts +104 -0
  83. package/dist_ts/protocols/protocol.sane.js +597 -0
  84. package/dist_ts/protocols/protocol.snmp.d.ts +133 -0
  85. package/dist_ts/protocols/protocol.snmp.js +333 -0
  86. package/dist_ts/protocols/protocol.upnp.d.ts +207 -0
  87. package/dist_ts/protocols/protocol.upnp.js +442 -0
  88. package/dist_ts/protocols/protocol.upssnmp.d.ts +160 -0
  89. package/dist_ts/protocols/protocol.upssnmp.js +263 -0
  90. package/npmextra.json +24 -0
  91. package/package.json +39 -0
  92. package/readme.hints.md +115 -0
  93. package/readme.md +624 -0
  94. package/test/test.ts +261 -0
  95. package/ts/00_commitinfo_data.ts +8 -0
  96. package/ts/device/device.classes.device.ts +454 -0
  97. package/ts/devicemanager.classes.devicemanager.ts +1219 -0
  98. package/ts/discovery/discovery.classes.homeassistant.ts +376 -0
  99. package/ts/discovery/discovery.classes.mdns.ts +347 -0
  100. package/ts/discovery/discovery.classes.networkscanner.ts +648 -0
  101. package/ts/discovery/discovery.classes.ssdp.ts +357 -0
  102. package/ts/factories/index.ts +696 -0
  103. package/ts/features/feature.abstract.ts +251 -0
  104. package/ts/features/feature.camera.ts +214 -0
  105. package/ts/features/feature.climate.ts +407 -0
  106. package/ts/features/feature.cover.ts +278 -0
  107. package/ts/features/feature.fan.ts +296 -0
  108. package/ts/features/feature.light.ts +369 -0
  109. package/ts/features/feature.lock.ts +223 -0
  110. package/ts/features/feature.playback.ts +246 -0
  111. package/ts/features/feature.power.ts +231 -0
  112. package/ts/features/feature.print.ts +297 -0
  113. package/ts/features/feature.scan.ts +370 -0
  114. package/ts/features/feature.sensor.ts +202 -0
  115. package/ts/features/feature.snmp.ts +235 -0
  116. package/ts/features/feature.switch.ts +170 -0
  117. package/ts/features/feature.volume.ts +256 -0
  118. package/ts/features/index.ts +25 -0
  119. package/ts/helpers/helpers.iprange.ts +172 -0
  120. package/ts/helpers/helpers.retry.ts +100 -0
  121. package/ts/index.ts +165 -0
  122. package/ts/interfaces/feature.interfaces.ts +359 -0
  123. package/ts/interfaces/homeassistant.interfaces.ts +666 -0
  124. package/ts/interfaces/index.ts +421 -0
  125. package/ts/interfaces/smarthome.interfaces.ts +421 -0
  126. package/ts/paths.ts +5 -0
  127. package/ts/plugins.ts +41 -0
  128. package/ts/protocols/index.ts +59 -0
  129. package/ts/protocols/protocol.escl.ts +439 -0
  130. package/ts/protocols/protocol.homeassistant.ts +737 -0
  131. package/ts/protocols/protocol.ipp.ts +329 -0
  132. package/ts/protocols/protocol.nut.ts +471 -0
  133. package/ts/protocols/protocol.sane.ts +694 -0
  134. package/ts/protocols/protocol.snmp.ts +439 -0
  135. package/ts/protocols/protocol.upnp.ts +627 -0
  136. package/ts/protocols/protocol.upssnmp.ts +377 -0
  137. package/tsconfig.json +12 -0
@@ -0,0 +1,535 @@
1
+ import * as plugins from '../plugins.js';
2
+ import { EsclProtocol } from '../protocols/index.js';
3
+ import { cidrToIps, ipRangeToIps, isValidIp, } from '../helpers/helpers.iprange.js';
4
+ /**
5
+ * Default ports to probe for device discovery
6
+ */
7
+ const DEFAULT_PORTS = [
8
+ 631, // IPP printers
9
+ 80, // eSCL scanners (HTTP)
10
+ 443, // eSCL scanners (HTTPS)
11
+ 6566, // SANE scanners
12
+ 9100, // JetDirect printers
13
+ 7000, // AirPlay speakers
14
+ 5000, // AirPlay control / RTSP
15
+ 3689, // DAAP (iTunes/AirPlay 2)
16
+ 1400, // Sonos speakers
17
+ 8009, // Chromecast devices
18
+ ];
19
+ /**
20
+ * Default scan options
21
+ */
22
+ const DEFAULT_OPTIONS = {
23
+ concurrency: 50,
24
+ timeout: 2000,
25
+ ports: DEFAULT_PORTS,
26
+ probeEscl: true,
27
+ probeIpp: true,
28
+ probeSane: true,
29
+ probeAirplay: true,
30
+ probeSonos: true,
31
+ probeChromecast: true,
32
+ };
33
+ /**
34
+ * Simple concurrency limiter
35
+ */
36
+ class ConcurrencyLimiter {
37
+ limit;
38
+ running = 0;
39
+ queue = [];
40
+ constructor(limit) {
41
+ this.limit = limit;
42
+ }
43
+ async run(fn) {
44
+ while (this.running >= this.limit) {
45
+ await new Promise((resolve) => this.queue.push(resolve));
46
+ }
47
+ this.running++;
48
+ try {
49
+ return await fn();
50
+ }
51
+ finally {
52
+ this.running--;
53
+ const next = this.queue.shift();
54
+ if (next)
55
+ next();
56
+ }
57
+ }
58
+ }
59
+ /**
60
+ * Network Scanner - scans IP ranges for scanners and printers
61
+ */
62
+ export class NetworkScanner extends plugins.events.EventEmitter {
63
+ cancelled = false;
64
+ scanning = false;
65
+ /**
66
+ * Check if a scan is currently in progress
67
+ */
68
+ get isScanning() {
69
+ return this.scanning;
70
+ }
71
+ /**
72
+ * Scan a network range for devices
73
+ */
74
+ async scan(options) {
75
+ if (this.scanning) {
76
+ throw new Error('A scan is already in progress');
77
+ }
78
+ this.cancelled = false;
79
+ this.scanning = true;
80
+ const opts = { ...DEFAULT_OPTIONS, ...options };
81
+ const results = [];
82
+ try {
83
+ // Get list of IPs to scan
84
+ const ips = this.resolveIps(options);
85
+ if (ips.length === 0) {
86
+ throw new Error('No IPs to scan. Provide ipRange, or startIp and endIp.');
87
+ }
88
+ const limiter = new ConcurrencyLimiter(opts.concurrency);
89
+ let scanned = 0;
90
+ // Progress tracking
91
+ const emitProgress = (currentIp) => {
92
+ const progress = {
93
+ total: ips.length,
94
+ scanned,
95
+ percentage: Math.round((scanned / ips.length) * 100),
96
+ currentIp,
97
+ devicesFound: results.reduce((sum, r) => sum + r.devices.length, 0),
98
+ };
99
+ this.emit('progress', progress);
100
+ };
101
+ emitProgress();
102
+ // Scan all IPs with concurrency limit
103
+ const scanPromises = ips.map((ip) => limiter.run(async () => {
104
+ if (this.cancelled)
105
+ return;
106
+ const devices = await this.probeIp(ip, opts);
107
+ scanned++;
108
+ if (devices.length > 0) {
109
+ const result = { address: ip, devices };
110
+ results.push(result);
111
+ this.emit('device:found', result);
112
+ }
113
+ emitProgress(ip);
114
+ }));
115
+ await Promise.all(scanPromises);
116
+ if (this.cancelled) {
117
+ this.emit('cancelled');
118
+ }
119
+ else {
120
+ this.emit('complete', results);
121
+ }
122
+ return results;
123
+ }
124
+ finally {
125
+ this.scanning = false;
126
+ }
127
+ }
128
+ /**
129
+ * Cancel an ongoing scan
130
+ */
131
+ async cancel() {
132
+ if (this.scanning) {
133
+ this.cancelled = true;
134
+ }
135
+ }
136
+ /**
137
+ * Resolve IPs from options (CIDR or start/end range)
138
+ */
139
+ resolveIps(options) {
140
+ if (options.ipRange) {
141
+ return cidrToIps(options.ipRange);
142
+ }
143
+ if (options.startIp && options.endIp) {
144
+ return ipRangeToIps(options.startIp, options.endIp);
145
+ }
146
+ if (options.startIp && !options.endIp) {
147
+ // Single IP
148
+ if (isValidIp(options.startIp)) {
149
+ return [options.startIp];
150
+ }
151
+ }
152
+ return [];
153
+ }
154
+ /**
155
+ * Probe a single IP address for devices
156
+ */
157
+ async probeIp(ip, opts) {
158
+ const devices = [];
159
+ const timeout = opts.timeout;
160
+ // First, do a quick port scan to see which ports are open
161
+ const openPorts = await this.scanPorts(ip, opts.ports, timeout / 2);
162
+ if (openPorts.length === 0 || this.cancelled) {
163
+ return devices;
164
+ }
165
+ // Probe each open port for specific protocols
166
+ const probePromises = [];
167
+ for (const port of openPorts) {
168
+ // IPP probe (port 631)
169
+ if (opts.probeIpp && port === 631) {
170
+ probePromises.push(this.probeIpp(ip, port, timeout).then((device) => {
171
+ if (device)
172
+ devices.push(device);
173
+ }));
174
+ }
175
+ // eSCL probe (ports 80, 443)
176
+ if (opts.probeEscl && (port === 80 || port === 443)) {
177
+ probePromises.push(this.probeEscl(ip, port, timeout).then((device) => {
178
+ if (device)
179
+ devices.push(device);
180
+ }));
181
+ }
182
+ // SANE probe (port 6566)
183
+ if (opts.probeSane && port === 6566) {
184
+ probePromises.push(this.probeSane(ip, port, timeout).then((device) => {
185
+ if (device)
186
+ devices.push(device);
187
+ }));
188
+ }
189
+ // JetDirect probe (port 9100) - just mark as raw printer
190
+ if (port === 9100) {
191
+ devices.push({
192
+ type: 'printer',
193
+ protocol: 'jetdirect',
194
+ port: 9100,
195
+ name: `Raw Printer at ${ip}`,
196
+ });
197
+ }
198
+ // AirPlay probe (port 7000) - try HTTP endpoints
199
+ if (opts.probeAirplay && port === 7000) {
200
+ probePromises.push(this.probeAirplay(ip, port, timeout).then((device) => {
201
+ if (device)
202
+ devices.push(device);
203
+ }));
204
+ }
205
+ // AirPlay ports 5000 (RTSP) and 3689 (DAAP) - if open, it's likely an AirPlay device
206
+ if (opts.probeAirplay && (port === 5000 || port === 3689)) {
207
+ devices.push({
208
+ type: 'speaker',
209
+ protocol: 'airplay',
210
+ port,
211
+ name: `AirPlay Device at ${ip}`,
212
+ });
213
+ }
214
+ // Sonos probe (port 1400)
215
+ if (opts.probeSonos && port === 1400) {
216
+ probePromises.push(this.probeSonos(ip, port, timeout).then((device) => {
217
+ if (device)
218
+ devices.push(device);
219
+ }));
220
+ }
221
+ // Chromecast probe (port 8009)
222
+ if (opts.probeChromecast && port === 8009) {
223
+ probePromises.push(this.probeChromecast(ip, port, timeout).then((device) => {
224
+ if (device)
225
+ devices.push(device);
226
+ }));
227
+ }
228
+ }
229
+ await Promise.all(probePromises);
230
+ return devices;
231
+ }
232
+ /**
233
+ * Quick TCP port scan
234
+ */
235
+ async scanPorts(ip, ports, timeout) {
236
+ const openPorts = [];
237
+ const scanPromises = ports.map(async (port) => {
238
+ const isOpen = await this.isPortOpen(ip, port, timeout);
239
+ if (isOpen) {
240
+ openPorts.push(port);
241
+ }
242
+ });
243
+ await Promise.all(scanPromises);
244
+ return openPorts;
245
+ }
246
+ /**
247
+ * Check if a TCP port is open
248
+ */
249
+ isPortOpen(ip, port, timeout) {
250
+ return new Promise((resolve) => {
251
+ const socket = new plugins.net.Socket();
252
+ const timer = setTimeout(() => {
253
+ socket.destroy();
254
+ resolve(false);
255
+ }, timeout);
256
+ socket.on('connect', () => {
257
+ clearTimeout(timer);
258
+ socket.destroy();
259
+ resolve(true);
260
+ });
261
+ socket.on('error', () => {
262
+ clearTimeout(timer);
263
+ socket.destroy();
264
+ resolve(false);
265
+ });
266
+ socket.connect(port, ip);
267
+ });
268
+ }
269
+ /**
270
+ * Probe for IPP printer using a simple HTTP check
271
+ * We avoid using the full ipp library for probing since it can hang and produce noisy output
272
+ */
273
+ async probeIpp(ip, port, timeout) {
274
+ try {
275
+ // Use a simple HTTP OPTIONS or POST to check if IPP endpoint exists
276
+ // IPP uses HTTP POST to /ipp/print or /ipp/printer
277
+ const controller = new AbortController();
278
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
279
+ try {
280
+ const response = await fetch(`http://${ip}:${port}/ipp/print`, {
281
+ method: 'POST',
282
+ headers: {
283
+ 'Content-Type': 'application/ipp',
284
+ },
285
+ body: Buffer.from([
286
+ // Minimal IPP Get-Printer-Attributes request
287
+ 0x01, 0x01, // IPP version 1.1
288
+ 0x00, 0x0b, // operation-id: Get-Printer-Attributes
289
+ 0x00, 0x00, 0x00, 0x01, // request-id: 1
290
+ 0x01, // operation-attributes-tag
291
+ 0x47, // charset
292
+ 0x00, 0x12, // name-length: 18
293
+ ...Buffer.from('attributes-charset'),
294
+ 0x00, 0x05, // value-length: 5
295
+ ...Buffer.from('utf-8'),
296
+ 0x48, // naturalLanguage
297
+ 0x00, 0x1b, // name-length: 27
298
+ ...Buffer.from('attributes-natural-language'),
299
+ 0x00, 0x05, // value-length: 5
300
+ ...Buffer.from('en-us'),
301
+ 0x03, // end-of-attributes-tag
302
+ ]),
303
+ signal: controller.signal,
304
+ });
305
+ clearTimeout(timeoutId);
306
+ // If we get a response with application/ipp content type, it's likely an IPP printer
307
+ const contentType = response.headers.get('content-type') || '';
308
+ if (response.ok || contentType.includes('application/ipp')) {
309
+ return {
310
+ type: 'printer',
311
+ protocol: 'ipp',
312
+ port,
313
+ name: `IPP Printer at ${ip}`,
314
+ model: undefined,
315
+ };
316
+ }
317
+ }
318
+ catch (fetchErr) {
319
+ clearTimeout(timeoutId);
320
+ // Fetch failed or was aborted
321
+ }
322
+ }
323
+ catch {
324
+ // Not an IPP printer
325
+ }
326
+ return null;
327
+ }
328
+ /**
329
+ * Probe for eSCL scanner
330
+ */
331
+ async probeEscl(ip, port, timeout) {
332
+ try {
333
+ const secure = port === 443;
334
+ const escl = new EsclProtocol(ip, port, secure);
335
+ const caps = await Promise.race([
336
+ escl.getCapabilities(),
337
+ new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), timeout)),
338
+ ]);
339
+ if (caps) {
340
+ return {
341
+ type: 'scanner',
342
+ protocol: 'escl',
343
+ port,
344
+ name: caps.makeAndModel || `eSCL Scanner at ${ip}`,
345
+ model: caps.makeAndModel,
346
+ };
347
+ }
348
+ }
349
+ catch {
350
+ // Not an eSCL scanner
351
+ }
352
+ return null;
353
+ }
354
+ /**
355
+ * Probe for SANE scanner (quick check)
356
+ */
357
+ async probeSane(ip, port, timeout) {
358
+ // SANE probing requires full protocol implementation
359
+ // For now, just check if port is open and assume it's SANE
360
+ // A more thorough implementation would send SANE_NET_INIT
361
+ try {
362
+ const isOpen = await this.isPortOpen(ip, port, timeout);
363
+ if (isOpen) {
364
+ return {
365
+ type: 'scanner',
366
+ protocol: 'sane',
367
+ port,
368
+ name: `SANE Scanner at ${ip}`,
369
+ };
370
+ }
371
+ }
372
+ catch {
373
+ // Not a SANE scanner
374
+ }
375
+ return null;
376
+ }
377
+ /**
378
+ * Probe for AirPlay speaker on port 7000
379
+ * Tries HTTP endpoints to identify the device. If no HTTP response,
380
+ * but port 7000 is open, it's still likely an AirPlay device.
381
+ */
382
+ async probeAirplay(ip, port, timeout) {
383
+ try {
384
+ const controller = new AbortController();
385
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
386
+ // Try /server-info first (older AirPlay devices)
387
+ try {
388
+ const response = await fetch(`http://${ip}:${port}/server-info`, {
389
+ signal: controller.signal,
390
+ });
391
+ clearTimeout(timeoutId);
392
+ if (response.ok) {
393
+ const text = await response.text();
394
+ // Parse model from plist if available
395
+ const modelMatch = text.match(/<key>model<\/key>\s*<string>([^<]+)<\/string>/);
396
+ const model = modelMatch?.[1];
397
+ return {
398
+ type: 'speaker',
399
+ protocol: 'airplay',
400
+ port,
401
+ name: model || `AirPlay Speaker at ${ip}`,
402
+ model,
403
+ };
404
+ }
405
+ }
406
+ catch {
407
+ clearTimeout(timeoutId);
408
+ }
409
+ // Try /info endpoint (some AirPlay 2 devices)
410
+ const controller2 = new AbortController();
411
+ const timeoutId2 = setTimeout(() => controller2.abort(), timeout);
412
+ try {
413
+ const response = await fetch(`http://${ip}:${port}/info`, {
414
+ signal: controller2.signal,
415
+ });
416
+ clearTimeout(timeoutId2);
417
+ if (response.ok) {
418
+ const text = await response.text();
419
+ // Try to parse model info
420
+ const modelMatch = text.match(/<key>model<\/key>\s*<string>([^<]+)<\/string>/);
421
+ const nameMatch = text.match(/<key>name<\/key>\s*<string>([^<]+)<\/string>/);
422
+ const model = modelMatch?.[1];
423
+ const name = nameMatch?.[1];
424
+ return {
425
+ type: 'speaker',
426
+ protocol: 'airplay',
427
+ port,
428
+ name: name || model || `AirPlay Speaker at ${ip}`,
429
+ model,
430
+ };
431
+ }
432
+ }
433
+ catch {
434
+ clearTimeout(timeoutId2);
435
+ }
436
+ // Port 7000 is open but no HTTP endpoints responded
437
+ // Still likely an AirPlay device (AirPlay 2 / HomePod)
438
+ return {
439
+ type: 'speaker',
440
+ protocol: 'airplay',
441
+ port,
442
+ name: `AirPlay Device at ${ip}`,
443
+ };
444
+ }
445
+ catch {
446
+ // Not an AirPlay speaker
447
+ }
448
+ return null;
449
+ }
450
+ /**
451
+ * Probe for Sonos speaker
452
+ */
453
+ async probeSonos(ip, port, timeout) {
454
+ try {
455
+ const controller = new AbortController();
456
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
457
+ try {
458
+ // Sonos devices respond to device description requests
459
+ const response = await fetch(`http://${ip}:${port}/xml/device_description.xml`, {
460
+ signal: controller.signal,
461
+ });
462
+ clearTimeout(timeoutId);
463
+ if (response.ok) {
464
+ const text = await response.text();
465
+ // Check if it's actually a Sonos device
466
+ if (text.includes('Sonos') || text.includes('schemas-upnp-org')) {
467
+ // Parse friendly name and model
468
+ const nameMatch = text.match(/<friendlyName>([^<]+)<\/friendlyName>/);
469
+ const modelMatch = text.match(/<modelName>([^<]+)<\/modelName>/);
470
+ return {
471
+ type: 'speaker',
472
+ protocol: 'sonos',
473
+ port,
474
+ name: nameMatch?.[1] || `Sonos Speaker at ${ip}`,
475
+ model: modelMatch?.[1],
476
+ };
477
+ }
478
+ }
479
+ }
480
+ catch {
481
+ clearTimeout(timeoutId);
482
+ }
483
+ }
484
+ catch {
485
+ // Not a Sonos speaker
486
+ }
487
+ return null;
488
+ }
489
+ /**
490
+ * Probe for Chromecast device
491
+ */
492
+ async probeChromecast(ip, port, timeout) {
493
+ try {
494
+ const controller = new AbortController();
495
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
496
+ try {
497
+ // Chromecast devices have an info endpoint on port 8008 (HTTP)
498
+ // Port 8009 is the Cast protocol port (TLS)
499
+ // Try fetching the eureka_info endpoint
500
+ const response = await fetch(`http://${ip}:8008/setup/eureka_info`, {
501
+ signal: controller.signal,
502
+ });
503
+ clearTimeout(timeoutId);
504
+ if (response.ok) {
505
+ const data = await response.json();
506
+ return {
507
+ type: 'speaker',
508
+ protocol: 'chromecast',
509
+ port,
510
+ name: data.name || `Chromecast at ${ip}`,
511
+ model: data.cast_build_revision || data.model_name,
512
+ };
513
+ }
514
+ }
515
+ catch {
516
+ clearTimeout(timeoutId);
517
+ }
518
+ // Alternative: just check if port 8009 is open (Cast protocol)
519
+ const isOpen = await this.isPortOpen(ip, port, timeout);
520
+ if (isOpen) {
521
+ return {
522
+ type: 'speaker',
523
+ protocol: 'chromecast',
524
+ port,
525
+ name: `Chromecast at ${ip}`,
526
+ };
527
+ }
528
+ }
529
+ catch {
530
+ // Not a Chromecast
531
+ }
532
+ return null;
533
+ }
534
+ }
535
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGlzY292ZXJ5LmNsYXNzZXMubmV0d29ya3NjYW5uZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9kaXNjb3ZlcnkvZGlzY292ZXJ5LmNsYXNzZXMubmV0d29ya3NjYW5uZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxlQUFlLENBQUM7QUFDekMsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQ3JELE9BQU8sRUFDTCxTQUFTLEVBQ1QsWUFBWSxFQUNaLFNBQVMsR0FDVixNQUFNLCtCQUErQixDQUFDO0FBUXZDOztHQUVHO0FBQ0gsTUFBTSxhQUFhLEdBQUc7SUFDcEIsR0FBRyxFQUFJLGVBQWU7SUFDdEIsRUFBRSxFQUFLLHVCQUF1QjtJQUM5QixHQUFHLEVBQUksd0JBQXdCO0lBQy9CLElBQUksRUFBRyxnQkFBZ0I7SUFDdkIsSUFBSSxFQUFHLHFCQUFxQjtJQUM1QixJQUFJLEVBQUcsbUJBQW1CO0lBQzFCLElBQUksRUFBRyx5QkFBeUI7SUFDaEMsSUFBSSxFQUFHLDBCQUEwQjtJQUNqQyxJQUFJLEVBQUcsaUJBQWlCO0lBQ3hCLElBQUksRUFBRyxxQkFBcUI7Q0FDN0IsQ0FBQztBQUVGOztHQUVHO0FBQ0gsTUFBTSxlQUFlLEdBQXlFO0lBQzVGLFdBQVcsRUFBRSxFQUFFO0lBQ2YsT0FBTyxFQUFFLElBQUk7SUFDYixLQUFLLEVBQUUsYUFBYTtJQUNwQixTQUFTLEVBQUUsSUFBSTtJQUNmLFFBQVEsRUFBRSxJQUFJO0lBQ2QsU0FBUyxFQUFFLElBQUk7SUFDZixZQUFZLEVBQUUsSUFBSTtJQUNsQixVQUFVLEVBQUUsSUFBSTtJQUNoQixlQUFlLEVBQUUsSUFBSTtDQUN0QixDQUFDO0FBRUY7O0dBRUc7QUFDSCxNQUFNLGtCQUFrQjtJQUlGO0lBSFosT0FBTyxHQUFHLENBQUMsQ0FBQztJQUNaLEtBQUssR0FBbUIsRUFBRSxDQUFDO0lBRW5DLFlBQW9C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
@@ -0,0 +1,112 @@
1
+ import * as plugins from '../plugins.js';
2
+ /**
3
+ * SSDP service types for device discovery
4
+ */
5
+ export declare const SSDP_SERVICE_TYPES: {
6
+ ROOT: string;
7
+ MEDIA_RENDERER: string;
8
+ MEDIA_SERVER: string;
9
+ SONOS_ZONE_PLAYER: string;
10
+ BASIC_DEVICE: string;
11
+ INTERNET_GATEWAY: string;
12
+ };
13
+ /**
14
+ * SSDP discovered device information
15
+ */
16
+ export interface ISsdpDevice {
17
+ /** Unique service name (USN) */
18
+ usn: string;
19
+ /** Service type (ST) */
20
+ serviceType: string;
21
+ /** Location URL of device description */
22
+ location: string;
23
+ /** IP address */
24
+ address: string;
25
+ /** Port number */
26
+ port: number;
27
+ /** Device description (fetched from location) */
28
+ description?: ISsdpDeviceDescription;
29
+ /** Raw headers from SSDP response */
30
+ headers: Record<string, string>;
31
+ }
32
+ /**
33
+ * UPnP device description (from XML)
34
+ */
35
+ export interface ISsdpDeviceDescription {
36
+ deviceType: string;
37
+ friendlyName: string;
38
+ manufacturer: string;
39
+ manufacturerURL?: string;
40
+ modelDescription?: string;
41
+ modelName: string;
42
+ modelNumber?: string;
43
+ modelURL?: string;
44
+ serialNumber?: string;
45
+ UDN: string;
46
+ services: ISsdpService[];
47
+ icons?: ISsdpIcon[];
48
+ }
49
+ export interface ISsdpService {
50
+ serviceType: string;
51
+ serviceId: string;
52
+ SCPDURL: string;
53
+ controlURL: string;
54
+ eventSubURL: string;
55
+ }
56
+ export interface ISsdpIcon {
57
+ mimetype: string;
58
+ width: number;
59
+ height: number;
60
+ depth: number;
61
+ url: string;
62
+ }
63
+ /**
64
+ * SSDP Discovery service using node-ssdp
65
+ */
66
+ export declare class SsdpDiscovery extends plugins.events.EventEmitter {
67
+ private client;
68
+ private devices;
69
+ private running;
70
+ private searchInterval;
71
+ constructor();
72
+ /**
73
+ * Start SSDP discovery
74
+ */
75
+ start(serviceTypes?: string[]): Promise<void>;
76
+ /**
77
+ * Stop SSDP discovery
78
+ */
79
+ stop(): Promise<void>;
80
+ /**
81
+ * Check if discovery is running
82
+ */
83
+ get isRunning(): boolean;
84
+ /**
85
+ * Get all discovered devices
86
+ */
87
+ getDevices(): ISsdpDevice[];
88
+ /**
89
+ * Get devices by service type
90
+ */
91
+ getDevicesByType(serviceType: string): ISsdpDevice[];
92
+ /**
93
+ * Search for a specific service type
94
+ */
95
+ search(serviceType: string): void;
96
+ /**
97
+ * Handle SSDP response
98
+ */
99
+ private handleSsdpResponse;
100
+ /**
101
+ * Fetch and parse device description XML
102
+ */
103
+ private fetchDeviceDescription;
104
+ /**
105
+ * Parse UPnP device description XML
106
+ */
107
+ private parseDeviceDescription;
108
+ /**
109
+ * Make a UPnP SOAP request
110
+ */
111
+ soapRequest(controlUrl: string, serviceType: string, action: string, args?: Record<string, string | number>): Promise<string>;
112
+ }