@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.
- package/.gitea/workflows/default_tags.yaml +0 -21
- package/.smartconfig.json +69 -0
- package/changelog.md +38 -1
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/device/device.classes.device.d.ts +2 -2
- package/dist_ts/device/device.classes.device.js +3 -3
- package/dist_ts/devicemanager.classes.devicemanager.d.ts +17 -4
- package/dist_ts/devicemanager.classes.devicemanager.js +99 -11
- package/dist_ts/discovery/discovery.classes.mdns.js +46 -11
- package/dist_ts/discovery/discovery.classes.networkscanner.d.ts +5 -1
- package/dist_ts/discovery/discovery.classes.networkscanner.js +44 -3
- package/dist_ts/discovery/discovery.classes.ssdp.d.ts +52 -21
- package/dist_ts/discovery/discovery.classes.ssdp.js +407 -117
- package/dist_ts/events/events.observable.d.ts +15 -0
- package/dist_ts/events/events.observable.js +22 -0
- package/dist_ts/events/index.d.ts +1 -0
- package/dist_ts/events/index.js +2 -0
- package/dist_ts/factories/index.js +7 -5
- package/dist_ts/features/feature.abstract.d.ts +2 -2
- package/dist_ts/features/feature.abstract.js +3 -3
- package/dist_ts/features/feature.print.d.ts +3 -0
- package/dist_ts/features/feature.print.js +17 -2
- package/dist_ts/features/feature.scan.d.ts +5 -2
- package/dist_ts/features/feature.scan.js +98 -27
- package/dist_ts/index.d.ts +4 -2
- package/dist_ts/index.js +4 -2
- package/dist_ts/interfaces/feature.interfaces.d.ts +22 -2
- package/dist_ts/interfaces/index.d.ts +22 -4
- package/dist_ts/interfaces/index.js +1 -1
- package/dist_ts/plugins.d.ts +6 -15
- package/dist_ts/plugins.js +7 -19
- package/dist_ts/protocols/index.d.ts +2 -1
- package/dist_ts/protocols/index.js +4 -2
- package/dist_ts/protocols/protocol.brother.d.ts +67 -0
- package/dist_ts/protocols/protocol.brother.js +560 -0
- package/dist_ts/protocols/protocol.escl.d.ts +13 -4
- package/dist_ts/protocols/protocol.escl.js +345 -178
- package/dist_ts/protocols/protocol.ipp.d.ts +2 -0
- package/dist_ts/protocols/protocol.ipp.js +22 -2
- package/dist_ts/providers/index.d.ts +1 -0
- package/dist_ts/providers/index.js +2 -0
- package/dist_ts/providers/provider.smb.d.ts +60 -0
- package/dist_ts/providers/provider.smb.js +224 -0
- package/license.md +21 -0
- package/package.json +15 -20
- package/pnpm-workspace.yaml +6 -0
- package/readme.md +157 -78
- package/test/test.brother.node.ts +306 -0
- package/test/test.escl.node.ts +233 -0
- package/test/test.ipp.node.ts +75 -0
- package/test/{test.ts → test.node.ts} +37 -45
- package/test/test.smb.node.ts +123 -0
- package/test/test.ssdp.node.ts +978 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/device/device.classes.device.ts +2 -2
- package/ts/devicemanager.classes.devicemanager.ts +125 -12
- package/ts/discovery/discovery.classes.mdns.ts +40 -11
- package/ts/discovery/discovery.classes.networkscanner.ts +51 -2
- package/ts/discovery/discovery.classes.ssdp.ts +493 -130
- package/ts/events/events.observable.ts +31 -0
- package/ts/events/index.ts +4 -0
- package/ts/factories/index.ts +7 -5
- package/ts/features/feature.abstract.ts +2 -2
- package/ts/features/feature.print.ts +16 -1
- package/ts/features/feature.scan.ts +93 -27
- package/ts/index.ts +21 -0
- package/ts/interfaces/feature.interfaces.ts +23 -2
- package/ts/interfaces/index.ts +23 -4
- package/ts/plugins.ts +6 -25
- package/ts/protocols/index.ts +11 -1
- package/ts/protocols/protocol.brother.ts +648 -0
- package/ts/protocols/protocol.escl.ts +348 -191
- package/ts/protocols/protocol.ipp.ts +24 -1
- package/ts/providers/index.ts +7 -0
- package/ts/providers/provider.smb.ts +326 -0
- package/dist_ts/protocols/protocol.ipp.old.d.ts +0 -45
- package/dist_ts/protocols/protocol.ipp.old.js +0 -284
- package/npmextra.json +0 -24
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
* A device is a network endpoint that can have multiple features (capabilities)
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import * as plugins from '../plugins.js';
|
|
7
6
|
import type {
|
|
8
7
|
TFeatureType,
|
|
9
8
|
IFeatureInfo,
|
|
@@ -11,6 +10,7 @@ import type {
|
|
|
11
10
|
} from '../interfaces/feature.interfaces.js';
|
|
12
11
|
import type { TDeviceStatus, IRetryOptions } from '../interfaces/index.js';
|
|
13
12
|
import { Feature, type TDeviceReference } from '../features/feature.abstract.js';
|
|
13
|
+
import { ObservableEventEmitter } from '../events/index.js';
|
|
14
14
|
|
|
15
15
|
// ============================================================================
|
|
16
16
|
// Device Info Interface
|
|
@@ -69,7 +69,7 @@ export interface IDeviceCreateOptions {
|
|
|
69
69
|
* }
|
|
70
70
|
* ```
|
|
71
71
|
*/
|
|
72
|
-
export class UniversalDevice extends
|
|
72
|
+
export class UniversalDevice extends ObservableEventEmitter {
|
|
73
73
|
// ============================================================================
|
|
74
74
|
// Properties
|
|
75
75
|
// ============================================================================
|
|
@@ -40,6 +40,12 @@ import type {
|
|
|
40
40
|
TFeatureType,
|
|
41
41
|
IDeviceSelector,
|
|
42
42
|
} from './interfaces/index.js';
|
|
43
|
+
import { ObservableEventEmitter } from './events/index.js';
|
|
44
|
+
import {
|
|
45
|
+
SmbShareProvider,
|
|
46
|
+
type ISmbShareProviderDependencies,
|
|
47
|
+
type ISmbShareProviderOptions,
|
|
48
|
+
} from './providers/index.js';
|
|
43
49
|
|
|
44
50
|
/**
|
|
45
51
|
* Default device manager options
|
|
@@ -132,10 +138,12 @@ function makeDeviceRef(address: string, port: number, name: string): { id: strin
|
|
|
132
138
|
* via multiple methods (mDNS, SSDP, IP scan), features are merged into a
|
|
133
139
|
* single UniversalDevice instance.
|
|
134
140
|
*/
|
|
135
|
-
export class DeviceManager extends
|
|
141
|
+
export class DeviceManager extends ObservableEventEmitter {
|
|
136
142
|
private mdnsDiscovery: MdnsDiscovery;
|
|
137
143
|
private ssdpDiscovery: SsdpDiscovery;
|
|
138
144
|
private _networkScanner: NetworkScanner | null = null;
|
|
145
|
+
private readonly smbProviders = new Set<SmbShareProvider>();
|
|
146
|
+
private readonly smbProviderSubscriptions = new Map<SmbShareProvider, plugins.rxjs.Subscription>();
|
|
139
147
|
|
|
140
148
|
// Devices keyed by IP address for deduplication
|
|
141
149
|
private devicesByIp: Map<string, UniversalDevice> = new Map();
|
|
@@ -546,17 +554,55 @@ export class DeviceManager extends plugins.events.EventEmitter {
|
|
|
546
554
|
// ============================================================================
|
|
547
555
|
|
|
548
556
|
public async startDiscovery(): Promise<void> {
|
|
549
|
-
await Promise.
|
|
550
|
-
this.mdnsDiscovery.start(),
|
|
551
|
-
this.ssdpDiscovery.start(),
|
|
557
|
+
const startResults = await Promise.allSettled([
|
|
558
|
+
Promise.resolve().then(() => this.mdnsDiscovery.start()),
|
|
559
|
+
Promise.resolve().then(() => this.ssdpDiscovery.start()),
|
|
560
|
+
]);
|
|
561
|
+
const startupFailures = startResults
|
|
562
|
+
.filter((result): result is PromiseRejectedResult => result.status === 'rejected')
|
|
563
|
+
.map((result) => result.reason as unknown);
|
|
564
|
+
if (startupFailures.length === 0) {
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
const cleanupResults = await Promise.allSettled([
|
|
569
|
+
Promise.resolve().then(() => this.mdnsDiscovery.stop()),
|
|
570
|
+
Promise.resolve().then(() => this.ssdpDiscovery.stop()),
|
|
552
571
|
]);
|
|
572
|
+
const cleanupFailures = cleanupResults
|
|
573
|
+
.filter((result): result is PromiseRejectedResult => result.status === 'rejected')
|
|
574
|
+
.map((result) => result.reason as unknown);
|
|
575
|
+
|
|
576
|
+
if (startupFailures.length === 1 && cleanupFailures.length === 0) {
|
|
577
|
+
throw startupFailures[0];
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
const message = cleanupFailures.length > 0
|
|
581
|
+
? 'Device discovery failed to start and rollback cleanup was incomplete.'
|
|
582
|
+
: 'Multiple discovery transports failed to start.';
|
|
583
|
+
throw new AggregateError(
|
|
584
|
+
[...startupFailures, ...cleanupFailures],
|
|
585
|
+
message,
|
|
586
|
+
{ cause: startupFailures[0] }
|
|
587
|
+
);
|
|
553
588
|
}
|
|
554
589
|
|
|
555
590
|
public async stopDiscovery(): Promise<void> {
|
|
556
|
-
await Promise.
|
|
557
|
-
this.mdnsDiscovery.stop(),
|
|
558
|
-
this.ssdpDiscovery.stop(),
|
|
591
|
+
const stopResults = await Promise.allSettled([
|
|
592
|
+
Promise.resolve().then(() => this.mdnsDiscovery.stop()),
|
|
593
|
+
Promise.resolve().then(() => this.ssdpDiscovery.stop()),
|
|
559
594
|
]);
|
|
595
|
+
const stopFailures = stopResults
|
|
596
|
+
.filter((result): result is PromiseRejectedResult => result.status === 'rejected')
|
|
597
|
+
.map((result) => result.reason as unknown);
|
|
598
|
+
|
|
599
|
+
if (stopFailures.length > 0) {
|
|
600
|
+
throw new AggregateError(
|
|
601
|
+
stopFailures,
|
|
602
|
+
'One or more discovery transports failed to stop cleanly.',
|
|
603
|
+
{ cause: stopFailures[0] }
|
|
604
|
+
);
|
|
605
|
+
}
|
|
560
606
|
}
|
|
561
607
|
|
|
562
608
|
public get isDiscovering(): boolean {
|
|
@@ -734,6 +780,61 @@ export class DeviceManager extends plugins.events.EventEmitter {
|
|
|
734
780
|
return this.getDevicesWithFeature('snmp');
|
|
735
781
|
}
|
|
736
782
|
|
|
783
|
+
// ============================================================================
|
|
784
|
+
// Host-side SMB providers
|
|
785
|
+
// ============================================================================
|
|
786
|
+
|
|
787
|
+
/**
|
|
788
|
+
* Start an SMB server and forward its events through this manager as `smb:*`.
|
|
789
|
+
*/
|
|
790
|
+
public async provideSmbShares(
|
|
791
|
+
options: ISmbShareProviderOptions,
|
|
792
|
+
dependencies?: ISmbShareProviderDependencies
|
|
793
|
+
): Promise<SmbShareProvider> {
|
|
794
|
+
const provider = new SmbShareProvider(options, dependencies);
|
|
795
|
+
const subscription = provider.events$.subscribe((event) => {
|
|
796
|
+
this.emit(`smb:${event.name}`, ...event.args);
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
try {
|
|
800
|
+
await provider.start();
|
|
801
|
+
this.smbProviders.add(provider);
|
|
802
|
+
this.smbProviderSubscriptions.set(provider, subscription);
|
|
803
|
+
return provider;
|
|
804
|
+
} catch (error) {
|
|
805
|
+
subscription.unsubscribe();
|
|
806
|
+
throw error;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/** Stop one SMB provider created by this manager. */
|
|
811
|
+
public async stopSmbShareProvider(provider: SmbShareProvider): Promise<void> {
|
|
812
|
+
try {
|
|
813
|
+
await provider.stop();
|
|
814
|
+
} finally {
|
|
815
|
+
this.smbProviderSubscriptions.get(provider)?.unsubscribe();
|
|
816
|
+
this.smbProviderSubscriptions.delete(provider);
|
|
817
|
+
this.smbProviders.delete(provider);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
/** Stop every SMB provider owned by this manager. */
|
|
822
|
+
public async stopAllSmbShareProviders(): Promise<void> {
|
|
823
|
+
const results = await Promise.allSettled(
|
|
824
|
+
[...this.smbProviders].map((provider) => this.stopSmbShareProvider(provider))
|
|
825
|
+
);
|
|
826
|
+
const failures = results
|
|
827
|
+
.filter((result): result is PromiseRejectedResult => result.status === 'rejected')
|
|
828
|
+
.map((result) => result.reason as unknown);
|
|
829
|
+
if (failures.length > 0) {
|
|
830
|
+
throw new AggregateError(failures, 'One or more SMB providers failed to stop cleanly.');
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
public getSmbShareProviders(): SmbShareProvider[] {
|
|
835
|
+
return [...this.smbProviders];
|
|
836
|
+
}
|
|
837
|
+
|
|
737
838
|
// ============================================================================
|
|
738
839
|
// Manual Device Addition
|
|
739
840
|
// ============================================================================
|
|
@@ -769,7 +870,7 @@ export class DeviceManager extends plugins.events.EventEmitter {
|
|
|
769
870
|
public async addScanner(
|
|
770
871
|
address: string,
|
|
771
872
|
port: number,
|
|
772
|
-
protocol: 'escl' | 'sane' = 'escl',
|
|
873
|
+
protocol: 'brother' | 'escl' | 'sane' = 'escl',
|
|
773
874
|
name?: string
|
|
774
875
|
): Promise<UniversalDevice> {
|
|
775
876
|
const feature = new ScanFeature(
|
|
@@ -1033,7 +1134,7 @@ export class DeviceManager extends plugins.events.EventEmitter {
|
|
|
1033
1134
|
device = await this.addScanner(
|
|
1034
1135
|
result.address,
|
|
1035
1136
|
deviceInfo.port,
|
|
1036
|
-
deviceInfo.protocol as 'escl' | 'sane',
|
|
1137
|
+
deviceInfo.protocol as 'brother' | 'escl' | 'sane',
|
|
1037
1138
|
deviceInfo.name
|
|
1038
1139
|
);
|
|
1039
1140
|
} else if (deviceInfo.type === 'printer' && deviceInfo.protocol === 'ipp') {
|
|
@@ -1060,7 +1161,8 @@ export class DeviceManager extends plugins.events.EventEmitter {
|
|
|
1060
1161
|
|
|
1061
1162
|
/**
|
|
1062
1163
|
* Discover scanners in a subnet and add them to the manager.
|
|
1063
|
-
* Convenience method that focuses discovery on scanner protocols only
|
|
1164
|
+
* Convenience method that focuses discovery on scanner protocols only
|
|
1165
|
+
* (Brother native, eSCL/AirScan, and SANE).
|
|
1064
1166
|
*
|
|
1065
1167
|
* @param subnet CIDR notation subnet (e.g., '192.168.1.0/24')
|
|
1066
1168
|
* @param options Optional discovery settings
|
|
@@ -1080,6 +1182,7 @@ export class DeviceManager extends plugins.events.EventEmitter {
|
|
|
1080
1182
|
): Promise<UniversalDevice[]> {
|
|
1081
1183
|
await this.scanNetwork({
|
|
1082
1184
|
ipRange: subnet,
|
|
1185
|
+
probeBrother: true,
|
|
1083
1186
|
probeEscl: true,
|
|
1084
1187
|
probeSane: true,
|
|
1085
1188
|
probeIpp: false,
|
|
@@ -1107,6 +1210,7 @@ export class DeviceManager extends plugins.events.EventEmitter {
|
|
|
1107
1210
|
): Promise<UniversalDevice[]> {
|
|
1108
1211
|
await this.scanNetwork({
|
|
1109
1212
|
ipRange: subnet,
|
|
1213
|
+
probeBrother: false,
|
|
1110
1214
|
probeEscl: false,
|
|
1111
1215
|
probeSane: false,
|
|
1112
1216
|
probeIpp: true,
|
|
@@ -1179,10 +1283,19 @@ export class DeviceManager extends plugins.events.EventEmitter {
|
|
|
1179
1283
|
* Stop discovery and disconnect all devices
|
|
1180
1284
|
*/
|
|
1181
1285
|
public async shutdown(): Promise<void> {
|
|
1182
|
-
await
|
|
1183
|
-
|
|
1286
|
+
const results = await Promise.allSettled([
|
|
1287
|
+
this.stopDiscovery(),
|
|
1288
|
+
this.stopAllSmbShareProviders(),
|
|
1289
|
+
this.disconnectAll(),
|
|
1290
|
+
]);
|
|
1184
1291
|
this.devicesByIp.clear();
|
|
1185
1292
|
this.nameSourceByIp.clear();
|
|
1293
|
+
const failures = results
|
|
1294
|
+
.filter((result): result is PromiseRejectedResult => result.status === 'rejected')
|
|
1295
|
+
.map((result) => result.reason as unknown);
|
|
1296
|
+
if (failures.length > 0) {
|
|
1297
|
+
throw new AggregateError(failures, 'DeviceManager shutdown was incomplete.');
|
|
1298
|
+
}
|
|
1186
1299
|
}
|
|
1187
1300
|
}
|
|
1188
1301
|
|
|
@@ -117,20 +117,49 @@ export class MdnsDiscovery extends plugins.events.EventEmitter {
|
|
|
117
117
|
return;
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
120
|
+
const browsers = this.browsers;
|
|
121
|
+
const bonjour = this.bonjour;
|
|
122
|
+
const cleanupErrors: unknown[] = [];
|
|
124
123
|
this.browsers = [];
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
124
|
+
this.bonjour = null;
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
for (const browser of browsers) {
|
|
128
|
+
try {
|
|
129
|
+
browser.stop();
|
|
130
|
+
} catch (error) {
|
|
131
|
+
cleanupErrors.push(error);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
} finally {
|
|
135
|
+
try {
|
|
136
|
+
if (bonjour) {
|
|
137
|
+
await new Promise<void>((resolve, reject) => {
|
|
138
|
+
try {
|
|
139
|
+
bonjour.destroy((error?: Error) => error ? reject(error) : resolve());
|
|
140
|
+
} catch (error) {
|
|
141
|
+
reject(error);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
} catch (error) {
|
|
146
|
+
cleanupErrors.push(error);
|
|
147
|
+
} finally {
|
|
148
|
+
this.isRunning = false;
|
|
149
|
+
try {
|
|
150
|
+
this.emit('stopped');
|
|
151
|
+
} catch (error) {
|
|
152
|
+
cleanupErrors.push(error);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
130
155
|
}
|
|
131
156
|
|
|
132
|
-
|
|
133
|
-
|
|
157
|
+
if (cleanupErrors.length === 1) {
|
|
158
|
+
throw cleanupErrors[0];
|
|
159
|
+
}
|
|
160
|
+
if (cleanupErrors.length > 1) {
|
|
161
|
+
throw new AggregateError(cleanupErrors, 'Failed to stop mDNS discovery cleanly.');
|
|
162
|
+
}
|
|
134
163
|
}
|
|
135
164
|
|
|
136
165
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as plugins from '../plugins.js';
|
|
2
|
-
import { EsclProtocol } from '../protocols/index.js';
|
|
2
|
+
import { BrotherScanProtocol, EsclProtocol } from '../protocols/index.js';
|
|
3
3
|
import {
|
|
4
4
|
cidrToIps,
|
|
5
5
|
ipRangeToIps,
|
|
@@ -19,6 +19,7 @@ const DEFAULT_PORTS = [
|
|
|
19
19
|
631, // IPP printers
|
|
20
20
|
80, // eSCL scanners (HTTP)
|
|
21
21
|
443, // eSCL scanners (HTTPS)
|
|
22
|
+
54921, // Brother native network scanners
|
|
22
23
|
6566, // SANE scanners
|
|
23
24
|
9100, // JetDirect printers
|
|
24
25
|
7000, // AirPlay speakers
|
|
@@ -35,6 +36,7 @@ const DEFAULT_OPTIONS: Required<Omit<INetworkScanOptions, 'ipRange' | 'startIp'
|
|
|
35
36
|
concurrency: 50,
|
|
36
37
|
timeout: 2000,
|
|
37
38
|
ports: DEFAULT_PORTS,
|
|
39
|
+
probeBrother: true,
|
|
38
40
|
probeEscl: true,
|
|
39
41
|
probeIpp: true,
|
|
40
42
|
probeSane: true,
|
|
@@ -205,6 +207,15 @@ export class NetworkScanner extends plugins.events.EventEmitter {
|
|
|
205
207
|
const probePromises: Promise<void>[] = [];
|
|
206
208
|
|
|
207
209
|
for (const port of openPorts) {
|
|
210
|
+
// Brother native scan service
|
|
211
|
+
if (opts.probeBrother && port === 54921) {
|
|
212
|
+
probePromises.push(
|
|
213
|
+
this.probeBrother(ip, port, timeout).then((device) => {
|
|
214
|
+
if (device) devices.push(device);
|
|
215
|
+
})
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
208
219
|
// IPP probe (port 631)
|
|
209
220
|
if (opts.probeIpp && port === 631) {
|
|
210
221
|
probePromises.push(
|
|
@@ -282,6 +293,17 @@ export class NetworkScanner extends plugins.events.EventEmitter {
|
|
|
282
293
|
|
|
283
294
|
await Promise.all(probePromises);
|
|
284
295
|
|
|
296
|
+
// Prefer a vendor transport that enforces scan settings over standards
|
|
297
|
+
// endpoints whose firmware may silently downgrade those settings.
|
|
298
|
+
const protocolPriority: Partial<Record<INetworkScanDevice['protocol'], number>> = {
|
|
299
|
+
brother: 0,
|
|
300
|
+
escl: 1,
|
|
301
|
+
sane: 2,
|
|
302
|
+
};
|
|
303
|
+
devices.sort((left, right) =>
|
|
304
|
+
(protocolPriority[left.protocol] ?? 10) - (protocolPriority[right.protocol] ?? 10)
|
|
305
|
+
);
|
|
306
|
+
|
|
285
307
|
return devices;
|
|
286
308
|
}
|
|
287
309
|
|
|
@@ -330,9 +352,36 @@ export class NetworkScanner extends plugins.events.EventEmitter {
|
|
|
330
352
|
});
|
|
331
353
|
}
|
|
332
354
|
|
|
355
|
+
/**
|
|
356
|
+
* Probe for Brother's native network scan service.
|
|
357
|
+
*/
|
|
358
|
+
private async probeBrother(
|
|
359
|
+
ip: string,
|
|
360
|
+
port: number,
|
|
361
|
+
timeout: number
|
|
362
|
+
): Promise<INetworkScanDevice | null> {
|
|
363
|
+
const brother = new BrotherScanProtocol(ip, port, { timeout, busyTimeout: 0 });
|
|
364
|
+
try {
|
|
365
|
+
await brother.connect();
|
|
366
|
+
return {
|
|
367
|
+
type: 'scanner',
|
|
368
|
+
protocol: 'brother',
|
|
369
|
+
port,
|
|
370
|
+
name: `Brother Scanner at ${ip}`,
|
|
371
|
+
model: undefined,
|
|
372
|
+
};
|
|
373
|
+
} catch {
|
|
374
|
+
// A busy endpoint is not selected because connection would fail; eSCL
|
|
375
|
+
// can still be discovered as a fallback on the same device.
|
|
376
|
+
return null;
|
|
377
|
+
} finally {
|
|
378
|
+
await brother.disconnect();
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
333
382
|
/**
|
|
334
383
|
* Probe for IPP printer using a simple HTTP check
|
|
335
|
-
*
|
|
384
|
+
* Use a minimal request instead of an external IPP client for discovery probing.
|
|
336
385
|
*/
|
|
337
386
|
private async probeIpp(
|
|
338
387
|
ip: string,
|