@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,835 @@
1
+ /**
2
+ * Device Manager
3
+ * Unified device discovery and management using UniversalDevice with Features
4
+ */
5
+ import * as plugins from './plugins.js';
6
+ import { MdnsDiscovery, SERVICE_TYPES } from './discovery/discovery.classes.mdns.js';
7
+ import { NetworkScanner } from './discovery/discovery.classes.networkscanner.js';
8
+ import { SsdpDiscovery, SSDP_SERVICE_TYPES } from './discovery/discovery.classes.ssdp.js';
9
+ import { UniversalDevice } from './device/device.classes.device.js';
10
+ import { ScanFeature, PrintFeature, PlaybackFeature, VolumeFeature, PowerFeature, SnmpFeature, } from './features/index.js';
11
+ import { createScanner, createPrinter, createSnmpDevice, createUpsDevice, createSpeaker, createDlnaRenderer, } from './factories/index.js';
12
+ /**
13
+ * Default device manager options
14
+ */
15
+ const DEFAULT_OPTIONS = {
16
+ autoDiscovery: true,
17
+ discoveryTimeout: 10000,
18
+ enableRetry: true,
19
+ maxRetries: 5,
20
+ retryBaseDelay: 1000,
21
+ };
22
+ const NAME_SOURCE_PRIORITY = {
23
+ generic: 0, // IP-based placeholder names
24
+ manual: 1, // Manually added devices
25
+ airplay: 2, // AirPlay can have generic names
26
+ chromecast: 3, // Chromecast has decent names
27
+ mdns: 4, // mDNS for scanners/printers (usually good)
28
+ dlna: 5, // SSDP/UPnP MediaRenderer (good friendly names)
29
+ sonos: 6, // Sonos SSDP ZonePlayer (most authoritative)
30
+ };
31
+ /**
32
+ * Check if a name is a "real" name vs a generic placeholder
33
+ */
34
+ function isRealName(name) {
35
+ if (!name)
36
+ return false;
37
+ const lower = name.toLowerCase();
38
+ // Generic names that indicate no real name was discovered
39
+ return !(lower.includes(' at ') ||
40
+ lower.startsWith('device ') ||
41
+ lower.startsWith('scanner ') ||
42
+ lower.startsWith('printer ') ||
43
+ lower.startsWith('speaker ') ||
44
+ lower.startsWith('ups ') ||
45
+ lower.startsWith('snmp ') ||
46
+ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/.test(name) // Just an IP address
47
+ );
48
+ }
49
+ /**
50
+ * Choose the best name based on source priority
51
+ */
52
+ function shouldUpdateName(existingSource, newSource, existingName, newName) {
53
+ // If new name isn't real, don't update
54
+ if (!isRealName(newName))
55
+ return false;
56
+ // If no existing name or source, use new
57
+ if (!existingName || !existingSource)
58
+ return true;
59
+ // If existing name isn't real but new is, use new
60
+ if (!isRealName(existingName))
61
+ return true;
62
+ // Both are real names - use priority
63
+ const existingPriority = NAME_SOURCE_PRIORITY[existingSource] ?? 0;
64
+ const newPriority = NAME_SOURCE_PRIORITY[newSource] ?? 0;
65
+ return newPriority > existingPriority;
66
+ }
67
+ /**
68
+ * Create a device reference for feature construction
69
+ */
70
+ function makeDeviceRef(address, port, name) {
71
+ return {
72
+ id: `device:${address}`,
73
+ address,
74
+ port,
75
+ name,
76
+ };
77
+ }
78
+ /**
79
+ * Main Device Manager class
80
+ * Discovers and manages network devices using the UniversalDevice architecture
81
+ *
82
+ * Devices are deduplicated by IP address - if the same device is discovered
83
+ * via multiple methods (mDNS, SSDP, IP scan), features are merged into a
84
+ * single UniversalDevice instance.
85
+ */
86
+ export class DeviceManager extends plugins.events.EventEmitter {
87
+ mdnsDiscovery;
88
+ ssdpDiscovery;
89
+ _networkScanner = null;
90
+ // Devices keyed by IP address for deduplication
91
+ devicesByIp = new Map();
92
+ // Track name source for priority-based naming
93
+ nameSourceByIp = new Map();
94
+ options;
95
+ retryOptions;
96
+ constructor(options) {
97
+ super();
98
+ this.options = { ...DEFAULT_OPTIONS, ...options };
99
+ this.retryOptions = {
100
+ maxRetries: this.options.maxRetries,
101
+ baseDelay: this.options.retryBaseDelay,
102
+ maxDelay: 16000,
103
+ multiplier: 2,
104
+ jitter: true,
105
+ };
106
+ this.mdnsDiscovery = new MdnsDiscovery({
107
+ timeout: this.options.discoveryTimeout,
108
+ });
109
+ this.ssdpDiscovery = new SsdpDiscovery();
110
+ this.setupDiscoveryEvents();
111
+ this.setupSsdpDiscoveryEvents();
112
+ }
113
+ // ============================================================================
114
+ // Device Registration (with deduplication by IP)
115
+ // ============================================================================
116
+ /**
117
+ * Register or merge a device by IP address
118
+ * Returns the device (existing or new) and whether it's newly created
119
+ */
120
+ registerDevice(address, port, name, manufacturer, model, feature, nameSource) {
121
+ const existing = this.devicesByIp.get(address);
122
+ if (existing) {
123
+ // Update name if new source has higher priority
124
+ const existingSource = this.nameSourceByIp.get(address);
125
+ if (shouldUpdateName(existingSource, nameSource, existing.name, name)) {
126
+ existing.name = name;
127
+ this.nameSourceByIp.set(address, nameSource);
128
+ }
129
+ // Update manufacturer/model if we have better info
130
+ if (manufacturer && !existing.manufacturer) {
131
+ existing.manufacturer = manufacturer;
132
+ }
133
+ if (model && !existing.model) {
134
+ existing.model = model;
135
+ }
136
+ // Add feature if not already present
137
+ if (!existing.hasFeature(feature.type)) {
138
+ existing.addFeature(feature);
139
+ return { device: existing, isNew: false, featureAdded: true };
140
+ }
141
+ return { device: existing, isNew: false, featureAdded: false };
142
+ }
143
+ // Create new device
144
+ const device = new UniversalDevice(address, port, {
145
+ name: name || `Device at ${address}`,
146
+ manufacturer,
147
+ model,
148
+ retryOptions: this.options.enableRetry ? this.retryOptions : undefined,
149
+ });
150
+ device.addFeature(feature);
151
+ this.devicesByIp.set(address, device);
152
+ this.nameSourceByIp.set(address, isRealName(name) ? nameSource : 'generic');
153
+ return { device, isNew: true, featureAdded: true };
154
+ }
155
+ // ============================================================================
156
+ // Discovery Event Handlers
157
+ // ============================================================================
158
+ setupDiscoveryEvents() {
159
+ this.mdnsDiscovery.on('device:found', (device) => {
160
+ this.handleMdnsDeviceFound(device);
161
+ });
162
+ this.mdnsDiscovery.on('device:lost', (device) => {
163
+ this.handleDeviceLost(device.address);
164
+ });
165
+ this.mdnsDiscovery.on('started', () => {
166
+ this.emit('discovery:started');
167
+ });
168
+ this.mdnsDiscovery.on('stopped', () => {
169
+ this.emit('discovery:stopped');
170
+ });
171
+ }
172
+ setupSsdpDiscoveryEvents() {
173
+ this.ssdpDiscovery.on('device:found', (device) => {
174
+ this.handleSsdpDeviceFound(device);
175
+ });
176
+ this.ssdpDiscovery.on('started', () => {
177
+ this.emit('ssdp:started');
178
+ });
179
+ this.ssdpDiscovery.on('stopped', () => {
180
+ this.emit('ssdp:stopped');
181
+ });
182
+ }
183
+ handleMdnsDeviceFound(discovered) {
184
+ const manufacturer = discovered.txtRecords['usb_MFG'] || discovered.txtRecords['mfg'];
185
+ const model = discovered.txtRecords['usb_MDL'] || discovered.txtRecords['mdl'] || discovered.txtRecords['ty'];
186
+ if (discovered.type === 'scanner') {
187
+ const protocol = discovered.protocol === 'ipp' ? 'escl' : discovered.protocol;
188
+ const isSecure = discovered.txtRecords['TLS'] === '1' || (protocol === 'escl' && discovered.port === 443);
189
+ const feature = new ScanFeature(makeDeviceRef(discovered.address, discovered.port, discovered.name), discovered.port, {
190
+ protocol,
191
+ secure: isSecure,
192
+ supportedFormats: this.parseScanFormats(discovered.txtRecords),
193
+ supportedResolutions: this.parseScanResolutions(discovered.txtRecords),
194
+ supportedColorModes: this.parseScanColorModes(discovered.txtRecords),
195
+ supportedSources: this.parseScanSources(discovered.txtRecords),
196
+ hasAdf: discovered.txtRecords['is']?.includes('adf') || false,
197
+ hasDuplex: discovered.txtRecords['is']?.includes('duplex') || false,
198
+ });
199
+ const { device, isNew, featureAdded } = this.registerDevice(discovered.address, discovered.port, discovered.name, manufacturer, model, feature, 'mdns');
200
+ if (isNew || featureAdded) {
201
+ this.emit('device:found', { device, featureType: 'scan' });
202
+ }
203
+ }
204
+ else if (discovered.type === 'printer') {
205
+ const ippPath = discovered.txtRecords['rp'] || discovered.txtRecords['rfo'] || '/ipp/print';
206
+ const uri = `ipp://${discovered.address}:${discovered.port}${ippPath.startsWith('/') ? '' : '/'}${ippPath}`;
207
+ const feature = new PrintFeature(makeDeviceRef(discovered.address, discovered.port, discovered.name), discovered.port, {
208
+ protocol: 'ipp',
209
+ uri,
210
+ supportsColor: discovered.txtRecords['Color'] === 'T' || discovered.txtRecords['color'] === 'true',
211
+ supportsDuplex: discovered.txtRecords['Duplex'] === 'T' || discovered.txtRecords['duplex'] === 'true',
212
+ });
213
+ const { device, isNew, featureAdded } = this.registerDevice(discovered.address, discovered.port, discovered.name, manufacturer, model, feature, 'mdns');
214
+ if (isNew || featureAdded) {
215
+ this.emit('device:found', { device, featureType: 'print' });
216
+ }
217
+ }
218
+ else if (discovered.type === 'speaker') {
219
+ const protocol = discovered.protocol;
220
+ const playbackFeature = new PlaybackFeature(makeDeviceRef(discovered.address, discovered.port, discovered.name), discovered.port, {
221
+ protocol,
222
+ supportsQueue: protocol === 'sonos',
223
+ supportsSeek: protocol !== 'airplay',
224
+ });
225
+ const volumeFeature = new VolumeFeature(makeDeviceRef(discovered.address, discovered.port, discovered.name), discovered.port, {
226
+ volumeProtocol: protocol,
227
+ minVolume: 0,
228
+ maxVolume: 100,
229
+ supportsMute: true,
230
+ });
231
+ const modelName = discovered.txtRecords['model'] || discovered.txtRecords['md'];
232
+ // Map protocol to name source - mDNS speaker protocols
233
+ const speakerNameSource = protocol === 'sonos' ? 'sonos' :
234
+ protocol === 'airplay' ? 'airplay' :
235
+ protocol === 'chromecast' ? 'chromecast' :
236
+ protocol === 'dlna' ? 'dlna' : 'mdns';
237
+ const { device, isNew } = this.registerDevice(discovered.address, discovered.port, discovered.name, undefined, modelName, playbackFeature, speakerNameSource);
238
+ // Also add volume feature
239
+ if (!device.hasFeature('volume')) {
240
+ device.addFeature(volumeFeature);
241
+ }
242
+ if (isNew) {
243
+ this.emit('device:found', { device, featureType: 'playback' });
244
+ }
245
+ }
246
+ }
247
+ handleSsdpDeviceFound(ssdpDevice) {
248
+ if (!ssdpDevice.description) {
249
+ return;
250
+ }
251
+ const serviceType = ssdpDevice.serviceType;
252
+ const deviceType = ssdpDevice.description.deviceType;
253
+ const desc = ssdpDevice.description;
254
+ // Check for DLNA Media Renderer
255
+ if (serviceType.includes('MediaRenderer') || deviceType.includes('MediaRenderer')) {
256
+ const port = parseInt(new URL(ssdpDevice.location).port) || 80;
257
+ const playbackFeature = new PlaybackFeature(makeDeviceRef(ssdpDevice.address, port, desc.friendlyName), port, {
258
+ protocol: 'dlna',
259
+ supportsQueue: false,
260
+ supportsSeek: true,
261
+ });
262
+ const volumeFeature = new VolumeFeature(makeDeviceRef(ssdpDevice.address, port, desc.friendlyName), port, {
263
+ volumeProtocol: 'dlna',
264
+ minVolume: 0,
265
+ maxVolume: 100,
266
+ supportsMute: true,
267
+ });
268
+ const { device, isNew } = this.registerDevice(ssdpDevice.address, port, desc.friendlyName, desc.manufacturer, desc.modelName, playbackFeature, 'dlna');
269
+ if (!device.hasFeature('volume')) {
270
+ device.addFeature(volumeFeature);
271
+ }
272
+ if (isNew) {
273
+ this.emit('device:found', { device, featureType: 'playback' });
274
+ }
275
+ }
276
+ // Check for Sonos ZonePlayer
277
+ if (serviceType.includes('ZonePlayer') || deviceType.includes('ZonePlayer')) {
278
+ const playbackFeature = new PlaybackFeature(makeDeviceRef(ssdpDevice.address, 1400, desc.friendlyName), 1400, {
279
+ protocol: 'sonos',
280
+ supportsQueue: true,
281
+ supportsSeek: true,
282
+ });
283
+ const volumeFeature = new VolumeFeature(makeDeviceRef(ssdpDevice.address, 1400, desc.friendlyName), 1400, {
284
+ volumeProtocol: 'sonos',
285
+ minVolume: 0,
286
+ maxVolume: 100,
287
+ supportsMute: true,
288
+ });
289
+ const { device, isNew } = this.registerDevice(ssdpDevice.address, 1400, desc.friendlyName, desc.manufacturer, desc.modelName, playbackFeature, 'sonos' // Sonos has highest naming priority
290
+ );
291
+ if (!device.hasFeature('volume')) {
292
+ device.addFeature(volumeFeature);
293
+ }
294
+ if (isNew) {
295
+ this.emit('device:found', { device, featureType: 'playback' });
296
+ }
297
+ }
298
+ }
299
+ async handleDeviceLost(address) {
300
+ const device = this.devicesByIp.get(address);
301
+ if (device) {
302
+ try {
303
+ await device.disconnect();
304
+ }
305
+ catch {
306
+ // Ignore disconnect errors
307
+ }
308
+ this.devicesByIp.delete(address);
309
+ this.nameSourceByIp.delete(address);
310
+ this.emit('device:lost', address);
311
+ }
312
+ }
313
+ // ============================================================================
314
+ // Parsing Helpers
315
+ // ============================================================================
316
+ parseScanFormats(txtRecords) {
317
+ const formats = [];
318
+ const pdl = txtRecords['pdl'] || txtRecords['DocumentFormat'] || '';
319
+ if (pdl.includes('jpeg') || pdl.includes('jpg'))
320
+ formats.push('jpeg');
321
+ if (pdl.includes('png'))
322
+ formats.push('png');
323
+ if (pdl.includes('pdf'))
324
+ formats.push('pdf');
325
+ if (pdl.includes('tiff'))
326
+ formats.push('tiff');
327
+ return formats.length > 0 ? formats : ['jpeg', 'png'];
328
+ }
329
+ parseScanResolutions(txtRecords) {
330
+ const rs = txtRecords['rs'] || '';
331
+ const parts = rs.split(',').map((s) => parseInt(s.trim())).filter((n) => !isNaN(n) && n > 0);
332
+ return parts.length > 0 ? parts : [75, 150, 300, 600];
333
+ }
334
+ parseScanColorModes(txtRecords) {
335
+ const cs = txtRecords['cs'] || txtRecords['ColorSpace'] || '';
336
+ const modes = [];
337
+ if (cs.includes('color') || cs.includes('RGB'))
338
+ modes.push('color');
339
+ if (cs.includes('gray') || cs.includes('grayscale'))
340
+ modes.push('grayscale');
341
+ if (cs.includes('binary') || cs.includes('bw'))
342
+ modes.push('blackwhite');
343
+ return modes.length > 0 ? modes : ['color', 'grayscale'];
344
+ }
345
+ parseScanSources(txtRecords) {
346
+ const is = txtRecords['is'] || txtRecords['InputSource'] || '';
347
+ const sources = [];
348
+ if (is.includes('platen') || is.includes('flatbed') || is === '') {
349
+ sources.push('flatbed');
350
+ }
351
+ if (is.includes('adf') || is.includes('feeder')) {
352
+ sources.push('adf');
353
+ }
354
+ if (is.includes('duplex')) {
355
+ sources.push('adf-duplex');
356
+ }
357
+ return sources.length > 0 ? sources : ['flatbed'];
358
+ }
359
+ // ============================================================================
360
+ // Discovery Control
361
+ // ============================================================================
362
+ async startDiscovery() {
363
+ await Promise.all([
364
+ this.mdnsDiscovery.start(),
365
+ this.ssdpDiscovery.start(),
366
+ ]);
367
+ }
368
+ async stopDiscovery() {
369
+ await Promise.all([
370
+ this.mdnsDiscovery.stop(),
371
+ this.ssdpDiscovery.stop(),
372
+ ]);
373
+ }
374
+ get isDiscovering() {
375
+ return this.mdnsDiscovery.running || this.ssdpDiscovery.isRunning;
376
+ }
377
+ // ============================================================================
378
+ // Device Access - All Devices
379
+ // ============================================================================
380
+ /**
381
+ * Get devices matching the selector criteria.
382
+ * Returns all devices if no selector provided, or devices matching all criteria.
383
+ * @param selector Optional criteria to filter devices
384
+ * @returns Array of matching devices (empty if no matches)
385
+ */
386
+ getDevices(selector) {
387
+ const devices = Array.from(this.devicesByIp.values());
388
+ if (!selector) {
389
+ return devices;
390
+ }
391
+ return devices.filter((device) => this.matchesSelector(device, selector));
392
+ }
393
+ /**
394
+ * Select exactly ONE device matching the criteria.
395
+ * Use this when you expect a specific device and want fail-fast behavior.
396
+ * @param selector Criteria to match the device
397
+ * @returns The matching device
398
+ * @throws Error if no device matches the selector
399
+ */
400
+ selectDevice(selector) {
401
+ const matches = this.getDevices(selector);
402
+ if (matches.length === 0) {
403
+ throw new Error(`No device found matching: ${JSON.stringify(selector)}`);
404
+ }
405
+ if (matches.length > 1 && !selector.address && !selector.id) {
406
+ // Multiple matches without unique identifier - log warning
407
+ console.warn(`Multiple devices (${matches.length}) match selector, using first: ${matches[0].name}`);
408
+ }
409
+ return matches[0];
410
+ }
411
+ /**
412
+ * Get device by ID
413
+ * @deprecated Use getDevices({ id }) or selectDevice({ id }) instead
414
+ */
415
+ getDevice(id) {
416
+ for (const device of this.devicesByIp.values()) {
417
+ if (device.id === id) {
418
+ return device;
419
+ }
420
+ }
421
+ return undefined;
422
+ }
423
+ /**
424
+ * Get device by address
425
+ * @deprecated Use getDevices({ address }) or selectDevice({ address }) instead
426
+ */
427
+ getDeviceByAddress(address) {
428
+ return this.devicesByIp.get(address);
429
+ }
430
+ /**
431
+ * Check if a device matches the selector criteria
432
+ */
433
+ matchesSelector(device, selector) {
434
+ // Identity checks (exact match)
435
+ if (selector.id && device.id !== selector.id) {
436
+ return false;
437
+ }
438
+ if (selector.address && device.address !== selector.address) {
439
+ return false;
440
+ }
441
+ // Attribute checks (partial match, case-insensitive)
442
+ if (selector.name && !device.name.toLowerCase().includes(selector.name.toLowerCase())) {
443
+ return false;
444
+ }
445
+ if (selector.model && !device.model?.toLowerCase().includes(selector.model.toLowerCase())) {
446
+ return false;
447
+ }
448
+ if (selector.manufacturer &&
449
+ !device.manufacturer?.toLowerCase().includes(selector.manufacturer.toLowerCase())) {
450
+ return false;
451
+ }
452
+ // Capability checks
453
+ if (selector.hasFeature && !device.hasFeature(selector.hasFeature)) {
454
+ return false;
455
+ }
456
+ if (selector.hasFeatures && !device.hasFeatures(selector.hasFeatures)) {
457
+ return false;
458
+ }
459
+ if (selector.hasAnyFeature && !device.hasAnyFeature(selector.hasAnyFeature)) {
460
+ return false;
461
+ }
462
+ return true;
463
+ }
464
+ // ============================================================================
465
+ // Device Access - By Feature Type
466
+ // ============================================================================
467
+ /**
468
+ * Get all devices that have a specific feature
469
+ */
470
+ getDevicesWithFeature(featureType) {
471
+ return this.getDevices().filter(device => device.hasFeature(featureType));
472
+ }
473
+ /**
474
+ * Get all devices that have ALL specified features
475
+ */
476
+ getDevicesWithFeatures(featureTypes) {
477
+ return this.getDevices().filter(device => device.hasFeatures(featureTypes));
478
+ }
479
+ /**
480
+ * Get all devices that have ANY of the specified features
481
+ */
482
+ getDevicesWithAnyFeature(featureTypes) {
483
+ return this.getDevices().filter(device => featureTypes.some(type => device.hasFeature(type)));
484
+ }
485
+ // ============================================================================
486
+ // Convenience Accessors (by feature type)
487
+ // ============================================================================
488
+ /**
489
+ * Get all scanner devices
490
+ */
491
+ getScanners() {
492
+ return this.getDevicesWithFeature('scan');
493
+ }
494
+ /**
495
+ * Get all printer devices
496
+ */
497
+ getPrinters() {
498
+ return this.getDevicesWithFeature('print');
499
+ }
500
+ /**
501
+ * Get all speaker devices (playback feature)
502
+ */
503
+ getSpeakers() {
504
+ return this.getDevicesWithFeature('playback');
505
+ }
506
+ /**
507
+ * Get all UPS/power devices
508
+ */
509
+ getUpsDevices() {
510
+ return this.getDevicesWithFeature('power');
511
+ }
512
+ /**
513
+ * Get all SNMP devices
514
+ */
515
+ getSnmpDevices() {
516
+ return this.getDevicesWithFeature('snmp');
517
+ }
518
+ // ============================================================================
519
+ // Manual Device Addition
520
+ // ============================================================================
521
+ /**
522
+ * Add a device
523
+ */
524
+ addDevice(device) {
525
+ const existing = this.devicesByIp.get(device.address);
526
+ if (existing) {
527
+ // Merge features into existing device
528
+ for (const feature of device.getFeatures()) {
529
+ if (!existing.hasFeature(feature.type)) {
530
+ existing.addFeature(feature);
531
+ }
532
+ }
533
+ // Update name if better using priority system
534
+ const existingSource = this.nameSourceByIp.get(device.address);
535
+ if (shouldUpdateName(existingSource, 'manual', existing.name, device.name)) {
536
+ existing.name = device.name;
537
+ this.nameSourceByIp.set(device.address, 'manual');
538
+ }
539
+ }
540
+ else {
541
+ this.devicesByIp.set(device.address, device);
542
+ this.nameSourceByIp.set(device.address, isRealName(device.name) ? 'manual' : 'generic');
543
+ this.emit('device:added', device);
544
+ }
545
+ }
546
+ /**
547
+ * Add a scanner manually
548
+ */
549
+ async addScanner(address, port, protocol = 'escl', name) {
550
+ const feature = new ScanFeature(makeDeviceRef(address, port, name || `Scanner at ${address}`), port, { protocol });
551
+ const { device } = this.registerDevice(address, port, name, undefined, undefined, feature, 'manual');
552
+ await device.connect();
553
+ return device;
554
+ }
555
+ /**
556
+ * Add a printer manually
557
+ */
558
+ async addPrinter(address, port = 631, name, ippPath) {
559
+ const path = ippPath || '/ipp/print';
560
+ const uri = `ipp://${address}:${port}${path.startsWith('/') ? '' : '/'}${path}`;
561
+ const feature = new PrintFeature(makeDeviceRef(address, port, name || `Printer at ${address}`), port, { protocol: 'ipp', uri });
562
+ const { device } = this.registerDevice(address, port, name, undefined, undefined, feature, 'manual');
563
+ await device.connect();
564
+ return device;
565
+ }
566
+ /**
567
+ * Add an SNMP device manually
568
+ */
569
+ async addSnmpDevice(address, port = 161, options) {
570
+ const feature = new SnmpFeature(makeDeviceRef(address, port, options?.name || `SNMP Device at ${address}`), port, { community: options?.community ?? 'public' });
571
+ const { device } = this.registerDevice(address, port, options?.name, undefined, undefined, feature, 'manual');
572
+ await device.connect();
573
+ return device;
574
+ }
575
+ /**
576
+ * Add a UPS device manually
577
+ */
578
+ async addUpsDevice(address, protocol, options) {
579
+ const port = options?.port ?? (protocol === 'nut' ? 3493 : 161);
580
+ const feature = new PowerFeature(makeDeviceRef(address, port, options?.name || `UPS at ${address}`), port, {
581
+ protocol,
582
+ upsName: options?.upsName,
583
+ community: options?.community,
584
+ });
585
+ const { device } = this.registerDevice(address, port, options?.name, undefined, undefined, feature, 'manual');
586
+ await device.connect();
587
+ return device;
588
+ }
589
+ /**
590
+ * Add a speaker manually
591
+ */
592
+ async addSpeaker(address, protocol, options) {
593
+ const defaultPort = protocol === 'sonos' ? 1400 : protocol === 'airplay' ? 7000 : protocol === 'chromecast' ? 8009 : 80;
594
+ const port = options?.port ?? defaultPort;
595
+ const deviceName = options?.name || `${protocol} at ${address}`;
596
+ const playbackFeature = new PlaybackFeature(makeDeviceRef(address, port, deviceName), port, {
597
+ protocol,
598
+ supportsQueue: protocol === 'sonos',
599
+ supportsSeek: protocol !== 'airplay',
600
+ });
601
+ const volumeFeature = new VolumeFeature(makeDeviceRef(address, port, deviceName), port, {
602
+ volumeProtocol: protocol,
603
+ minVolume: 0,
604
+ maxVolume: 100,
605
+ supportsMute: true,
606
+ });
607
+ // Use protocol as name source for speakers
608
+ const speakerNameSource = protocol === 'sonos' ? 'sonos' :
609
+ protocol === 'airplay' ? 'airplay' :
610
+ protocol === 'chromecast' ? 'chromecast' : 'dlna';
611
+ const { device } = this.registerDevice(address, port, options?.name, undefined, undefined, playbackFeature, speakerNameSource);
612
+ if (!device.hasFeature('volume')) {
613
+ device.addFeature(volumeFeature);
614
+ }
615
+ await device.connect();
616
+ return device;
617
+ }
618
+ // ============================================================================
619
+ // Feature Access
620
+ // ============================================================================
621
+ /**
622
+ * Get a feature from a device
623
+ */
624
+ getFeature(deviceId, featureType) {
625
+ const device = this.getDevice(deviceId);
626
+ return device?.getFeature(featureType);
627
+ }
628
+ /**
629
+ * Add a feature to a device
630
+ */
631
+ addFeature(deviceId, feature) {
632
+ const device = this.getDevice(deviceId);
633
+ if (device) {
634
+ device.addFeature(feature);
635
+ this.emit('feature:added', { device, feature });
636
+ return true;
637
+ }
638
+ return false;
639
+ }
640
+ /**
641
+ * Remove a feature from a device
642
+ */
643
+ async removeFeature(deviceId, featureType) {
644
+ const device = this.getDevice(deviceId);
645
+ if (device) {
646
+ const removed = await device.removeFeature(featureType);
647
+ if (removed) {
648
+ this.emit('feature:removed', { device, featureType });
649
+ }
650
+ return removed;
651
+ }
652
+ return false;
653
+ }
654
+ // ============================================================================
655
+ // Network Scanning
656
+ // ============================================================================
657
+ get networkScanner() {
658
+ if (!this._networkScanner) {
659
+ this._networkScanner = new NetworkScanner();
660
+ this.setupNetworkScannerEvents();
661
+ }
662
+ return this._networkScanner;
663
+ }
664
+ setupNetworkScannerEvents() {
665
+ if (!this._networkScanner)
666
+ return;
667
+ this._networkScanner.on('device:found', (result) => {
668
+ this.emit('network:device:found', result);
669
+ });
670
+ this._networkScanner.on('progress', (progress) => {
671
+ this.emit('network:progress', progress);
672
+ });
673
+ this._networkScanner.on('complete', (results) => {
674
+ this.emit('network:complete', results);
675
+ });
676
+ this._networkScanner.on('error', (error) => {
677
+ this.emit('error', error);
678
+ });
679
+ }
680
+ async scanNetwork(options) {
681
+ const results = await this.networkScanner.scan(options);
682
+ const foundDevices = [];
683
+ const seenAddresses = new Set();
684
+ for (const result of results) {
685
+ // Skip if we already processed this IP in this scan
686
+ if (seenAddresses.has(result.address))
687
+ continue;
688
+ seenAddresses.add(result.address);
689
+ for (const deviceInfo of result.devices) {
690
+ try {
691
+ let device = null;
692
+ if (deviceInfo.type === 'scanner') {
693
+ device = await this.addScanner(result.address, deviceInfo.port, deviceInfo.protocol, deviceInfo.name);
694
+ }
695
+ else if (deviceInfo.type === 'printer' && deviceInfo.protocol === 'ipp') {
696
+ device = await this.addPrinter(result.address, deviceInfo.port, deviceInfo.name);
697
+ }
698
+ else if (deviceInfo.type === 'speaker') {
699
+ device = await this.addSpeaker(result.address, deviceInfo.protocol, { name: deviceInfo.name, port: deviceInfo.port });
700
+ }
701
+ if (device && !foundDevices.includes(device)) {
702
+ foundDevices.push(device);
703
+ }
704
+ }
705
+ catch (error) {
706
+ this.emit('error', error instanceof Error ? error : new Error(String(error)));
707
+ }
708
+ }
709
+ }
710
+ return foundDevices;
711
+ }
712
+ /**
713
+ * Discover scanners in a subnet and add them to the manager.
714
+ * Convenience method that focuses discovery on scanner protocols only (eSCL, SANE).
715
+ *
716
+ * @param subnet CIDR notation subnet (e.g., '192.168.1.0/24')
717
+ * @param options Optional discovery settings
718
+ * @returns Array of discovered scanner devices
719
+ *
720
+ * @example
721
+ * ```typescript
722
+ * const scanners = await manager.discoverScanners('192.168.190.0/24');
723
+ * for (const scanner of scanners) {
724
+ * console.log(`Found: ${scanner.name} at ${scanner.address}`);
725
+ * }
726
+ * ```
727
+ */
728
+ async discoverScanners(subnet, options) {
729
+ await this.scanNetwork({
730
+ ipRange: subnet,
731
+ probeEscl: true,
732
+ probeSane: true,
733
+ probeIpp: false,
734
+ probeAirplay: false,
735
+ probeSonos: false,
736
+ probeChromecast: false,
737
+ concurrency: options?.concurrency ?? 50,
738
+ timeout: options?.timeout ?? 3000,
739
+ });
740
+ return this.getScanners();
741
+ }
742
+ /**
743
+ * Discover printers in a subnet and add them to the manager.
744
+ * Convenience method that focuses discovery on printer protocols only (IPP).
745
+ *
746
+ * @param subnet CIDR notation subnet (e.g., '192.168.1.0/24')
747
+ * @param options Optional discovery settings
748
+ * @returns Array of discovered printer devices
749
+ */
750
+ async discoverPrinters(subnet, options) {
751
+ await this.scanNetwork({
752
+ ipRange: subnet,
753
+ probeEscl: false,
754
+ probeSane: false,
755
+ probeIpp: true,
756
+ probeAirplay: false,
757
+ probeSonos: false,
758
+ probeChromecast: false,
759
+ concurrency: options?.concurrency ?? 50,
760
+ timeout: options?.timeout ?? 3000,
761
+ });
762
+ return this.getPrinters();
763
+ }
764
+ async cancelNetworkScan() {
765
+ if (this._networkScanner) {
766
+ await this._networkScanner.cancel();
767
+ }
768
+ }
769
+ get isNetworkScanning() {
770
+ return this._networkScanner?.isScanning ?? false;
771
+ }
772
+ // ============================================================================
773
+ // Device Removal and Cleanup
774
+ // ============================================================================
775
+ /**
776
+ * Remove a device by ID
777
+ */
778
+ async removeDevice(id) {
779
+ const device = this.getDevice(id);
780
+ if (device) {
781
+ await device.disconnect();
782
+ this.devicesByIp.delete(device.address);
783
+ this.nameSourceByIp.delete(device.address);
784
+ this.emit('device:removed', id);
785
+ return true;
786
+ }
787
+ return false;
788
+ }
789
+ /**
790
+ * Remove a device by address
791
+ */
792
+ async removeDeviceByAddress(address) {
793
+ const device = this.devicesByIp.get(address);
794
+ if (device) {
795
+ await device.disconnect();
796
+ this.devicesByIp.delete(address);
797
+ this.nameSourceByIp.delete(address);
798
+ this.emit('device:removed', device.id);
799
+ return true;
800
+ }
801
+ return false;
802
+ }
803
+ /**
804
+ * Disconnect all devices
805
+ */
806
+ async disconnectAll() {
807
+ const promises = [];
808
+ for (const device of this.devicesByIp.values()) {
809
+ promises.push(device.disconnect().catch(() => { }));
810
+ }
811
+ await Promise.all(promises);
812
+ }
813
+ /**
814
+ * Stop discovery and disconnect all devices
815
+ */
816
+ async shutdown() {
817
+ await this.stopDiscovery();
818
+ await this.disconnectAll();
819
+ this.devicesByIp.clear();
820
+ this.nameSourceByIp.clear();
821
+ }
822
+ }
823
+ // ============================================================================
824
+ // Exports
825
+ // ============================================================================
826
+ export {
827
+ // Discovery
828
+ MdnsDiscovery, NetworkScanner, SsdpDiscovery, SERVICE_TYPES, SSDP_SERVICE_TYPES,
829
+ // Universal Device
830
+ UniversalDevice,
831
+ // Features
832
+ ScanFeature, PrintFeature, PlaybackFeature, VolumeFeature, PowerFeature, SnmpFeature,
833
+ // Factories
834
+ createScanner, createPrinter, createSnmpDevice, createUpsDevice, createSpeaker, createDlnaRenderer, };
835
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGV2aWNlbWFuYWdlci5jbGFzc2VzLmRldmljZW1hbmFnZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9kZXZpY2VtYW5hZ2VyLmNsYXNzZXMuZGV2aWNlbWFuYWdlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7O0dBR0c7QUFFSCxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUN4QyxPQUFPLEVBQUUsYUFBYSxFQUFFLGFBQWEsRUFBRSxNQUFNLHVDQUF1QyxDQUFDO0FBQ3JGLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxpREFBaUQsQ0FBQztBQUNqRixPQUFPLEVBQUUsYUFBYSxFQUFFLGtCQUFrQixFQUFvQixNQUFNLHVDQUF1QyxDQUFDO0FBQzVHLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUNwRSxPQUFPLEVBQ0wsV0FBVyxFQUNYLFlBQVksRUFDWixlQUFlLEVBQ2YsYUFBYSxFQUNiLFlBQVksRUFDWixXQUFXLEdBQ1osTUFBTSxxQkFBcUIsQ0FBQztBQUU3QixPQUFPLEVBQ0wsYUFBYSxFQUNiLGFBQWEsRUFDYixnQkFBZ0IsRUFDaEIsZUFBZSxFQUNmLGFBQWEsRUFDYixrQkFBa0IsR0FPbkIsTUFBTSxzQkFBc0IsQ0FBQztBQVc5Qjs7R0FFRztBQUNILE1BQU0sZUFBZSxHQUFvQztJQUN2RCxhQUFhLEVBQUUsSUFBSTtJQUNuQixnQkFBZ0IsRUFBRSxLQUFLO0lBQ3ZCLFdBQVcsRUFBRSxJQUFJO0lBQ2pCLFVBQVUsRUFBRSxDQUFDO0lBQ2IsY0FBYyxFQUFFLElBQUk7Q0FDckIsQ0FBQztBQVFGLE1BQU0sb0JBQW9CLEdBQWdDO0lBQ3hELE9BQU8sRUFBRSxDQUFDLEVBQU0sNkJBQTZCO0lBQzdDLE1BQU0sRUFBRSxDQUFDLEVBQU8seUJBQXlCO0lBQ3pDLE9BQU8sRUFBRSxDQUFDLEVBQU0saUNBQWlDO0lBQ2pELFVBQVUsRUFBRSxDQUFDLEVBQUcsOEJBQThCO0lB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