@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
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
|
+
|
|
3
|
+
export interface IObservableEvent<TName extends string = string> {
|
|
4
|
+
name: TName;
|
|
5
|
+
args: readonly unknown[];
|
|
6
|
+
emittedAt: Date;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* EventEmitter-compatible base that mirrors every named event to RxJS.
|
|
11
|
+
*/
|
|
12
|
+
export class ObservableEventEmitter extends plugins.events.EventEmitter {
|
|
13
|
+
private readonly eventSubject = new plugins.rxjs.Subject<IObservableEvent>();
|
|
14
|
+
|
|
15
|
+
public readonly events$ = this.eventSubject.asObservable();
|
|
16
|
+
|
|
17
|
+
public override emit(eventName: string | symbol, ...args: unknown[]): boolean {
|
|
18
|
+
if (typeof eventName === 'string') {
|
|
19
|
+
this.eventSubject.next({
|
|
20
|
+
name: eventName,
|
|
21
|
+
args,
|
|
22
|
+
emittedAt: new Date(),
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
return super.emit(eventName, ...args);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
protected completeEventStream(): void {
|
|
29
|
+
this.eventSubject.complete();
|
|
30
|
+
}
|
|
31
|
+
}
|
package/ts/factories/index.ts
CHANGED
|
@@ -43,10 +43,12 @@ export function createScanner(
|
|
|
43
43
|
const isSecure = info.txtRecords['TLS'] === '1' || (protocol === 'escl' && info.port === 443);
|
|
44
44
|
|
|
45
45
|
// Parse capabilities from TXT records
|
|
46
|
-
const formats = parseScanFormats(info.txtRecords);
|
|
47
|
-
const resolutions = parseScanResolutions(info.txtRecords);
|
|
48
|
-
const colorModes = parseScanColorModes(info.txtRecords);
|
|
49
|
-
const sources =
|
|
46
|
+
const formats = protocol === 'brother' ? ['jpeg' as const] : parseScanFormats(info.txtRecords);
|
|
47
|
+
const resolutions = protocol === 'brother' ? [100, 150, 200, 300, 600] : parseScanResolutions(info.txtRecords);
|
|
48
|
+
const colorModes = protocol === 'brother' ? ['color' as const] : parseScanColorModes(info.txtRecords);
|
|
49
|
+
const sources = protocol === 'brother'
|
|
50
|
+
? ['flatbed' as const, 'adf' as const, 'adf-duplex' as const]
|
|
51
|
+
: parseScanSources(info.txtRecords);
|
|
50
52
|
|
|
51
53
|
const device = new UniversalDevice(info.address, info.port, {
|
|
52
54
|
name: info.name,
|
|
@@ -60,7 +62,7 @@ export function createScanner(
|
|
|
60
62
|
|
|
61
63
|
// Add scan feature
|
|
62
64
|
const scanFeature = new ScanFeature(device.getDeviceReference(), info.port, {
|
|
63
|
-
protocol: protocol as 'escl' | 'sane',
|
|
65
|
+
protocol: protocol as 'brother' | 'escl' | 'sane',
|
|
64
66
|
secure: isSecure,
|
|
65
67
|
supportedFormats: formats,
|
|
66
68
|
supportedResolutions: resolutions,
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
* Features are composable capabilities that can be attached to devices
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import * as plugins from '../plugins.js';
|
|
7
6
|
import type {
|
|
8
7
|
TFeatureType,
|
|
9
8
|
TFeatureState,
|
|
@@ -13,6 +12,7 @@ import type {
|
|
|
13
12
|
} from '../interfaces/feature.interfaces.js';
|
|
14
13
|
import type { IRetryOptions } from '../interfaces/index.js';
|
|
15
14
|
import { withRetry } from '../helpers/helpers.retry.js';
|
|
15
|
+
import { ObservableEventEmitter } from '../events/index.js';
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Forward reference to Device to avoid circular dependency
|
|
@@ -29,7 +29,7 @@ export type TDeviceReference = {
|
|
|
29
29
|
* Abstract base class for all features
|
|
30
30
|
* Provides common functionality for connection management, state tracking, and retry logic
|
|
31
31
|
*/
|
|
32
|
-
export abstract class Feature extends
|
|
32
|
+
export abstract class Feature extends ObservableEventEmitter implements IFeature {
|
|
33
33
|
/**
|
|
34
34
|
* The feature type identifier
|
|
35
35
|
*/
|
|
@@ -58,6 +58,9 @@ export class PrintFeature extends Feature {
|
|
|
58
58
|
// Capabilities
|
|
59
59
|
public supportsColor: boolean = true;
|
|
60
60
|
public supportsDuplex: boolean = false;
|
|
61
|
+
public airPrintSupported: boolean = false;
|
|
62
|
+
public supportedDocumentFormats: string[] = [];
|
|
63
|
+
public supportedResolutions: number[] = [300, 600];
|
|
61
64
|
public supportedMediaSizes: string[] = ['iso_a4_210x297mm', 'na_letter_8.5x11in'];
|
|
62
65
|
public supportedMediaTypes: string[] = ['stationery'];
|
|
63
66
|
public maxCopies: number = 99;
|
|
@@ -119,11 +122,13 @@ export class PrintFeature extends Feature {
|
|
|
119
122
|
*/
|
|
120
123
|
public async getCapabilities(): Promise<IPrintCapabilities> {
|
|
121
124
|
return {
|
|
125
|
+
airPrintSupported: this.airPrintSupported,
|
|
122
126
|
colorSupported: this.supportsColor,
|
|
123
127
|
duplexSupported: this.supportsDuplex,
|
|
128
|
+
documentFormats: this.supportedDocumentFormats,
|
|
124
129
|
mediaSizes: this.supportedMediaSizes,
|
|
125
130
|
mediaTypes: this.supportedMediaTypes,
|
|
126
|
-
resolutions:
|
|
131
|
+
resolutions: this.supportedResolutions,
|
|
127
132
|
maxCopies: this.maxCopies,
|
|
128
133
|
sidesSupported: this.supportedSides,
|
|
129
134
|
qualitySupported: this.supportedQualities,
|
|
@@ -212,6 +217,8 @@ export class PrintFeature extends Feature {
|
|
|
212
217
|
// ============================================================================
|
|
213
218
|
|
|
214
219
|
private updateCapabilitiesFromIpp(caps: IIppPrinterCapabilities): void {
|
|
220
|
+
this.airPrintSupported = caps.airPrintSupported;
|
|
221
|
+
this.supportedDocumentFormats = caps.documentFormatSupported;
|
|
215
222
|
this.supportsColor = caps.colorSupported;
|
|
216
223
|
// Derive duplexSupported from sidesSupported
|
|
217
224
|
this.supportsDuplex = caps.sidesSupported?.some(s =>
|
|
@@ -219,6 +226,12 @@ export class PrintFeature extends Feature {
|
|
|
219
226
|
) ?? false;
|
|
220
227
|
// Get max copies from range
|
|
221
228
|
this.maxCopies = caps.copiesSupported?.upper ?? 99;
|
|
229
|
+
const resolutions = caps.resolutionsSupported
|
|
230
|
+
.filter((resolution) => resolution.units === 'dpi' && resolution.crossFeed === resolution.feed)
|
|
231
|
+
.map((resolution) => resolution.crossFeed);
|
|
232
|
+
if (resolutions.length > 0) {
|
|
233
|
+
this.supportedResolutions = [...new Set(resolutions)].sort((left, right) => left - right);
|
|
234
|
+
}
|
|
222
235
|
|
|
223
236
|
if (caps.mediaSizeSupported && caps.mediaSizeSupported.length > 0) {
|
|
224
237
|
this.supportedMediaSizes = caps.mediaSizeSupported;
|
|
@@ -274,8 +287,10 @@ export class PrintFeature extends Feature {
|
|
|
274
287
|
...this.getBaseFeatureInfo(),
|
|
275
288
|
type: 'print',
|
|
276
289
|
protocol: this.protocol,
|
|
290
|
+
airPrintSupported: this.airPrintSupported,
|
|
277
291
|
supportsColor: this.supportsColor,
|
|
278
292
|
supportsDuplex: this.supportsDuplex,
|
|
293
|
+
supportedDocumentFormats: this.supportedDocumentFormats,
|
|
279
294
|
supportedMediaSizes: this.supportedMediaSizes,
|
|
280
295
|
};
|
|
281
296
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Scan Feature
|
|
3
|
-
* Provides document scanning capability using eSCL or SANE protocols
|
|
3
|
+
* Provides document scanning capability using Brother native, eSCL, or SANE protocols
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { Feature, type TDeviceReference } from './feature.abstract.js';
|
|
7
|
-
import { EsclProtocol, SaneProtocol } from '../protocols/index.js';
|
|
7
|
+
import { BrotherScanProtocol, EsclProtocol, SaneProtocol } from '../protocols/index.js';
|
|
8
|
+
import type { IEsclCapabilities } from '../interfaces/index.js';
|
|
8
9
|
import type {
|
|
9
10
|
TScanProtocol,
|
|
10
11
|
TScanFormat,
|
|
@@ -35,7 +36,7 @@ export interface IScanFeatureOptions extends IFeatureOptions {
|
|
|
35
36
|
/**
|
|
36
37
|
* Scan Feature - provides document scanning capability
|
|
37
38
|
*
|
|
38
|
-
* Wraps eSCL (AirScan) or SANE protocols
|
|
39
|
+
* Wraps Brother native, eSCL (AirScan), or SANE protocols behind one interface.
|
|
39
40
|
*
|
|
40
41
|
* @example
|
|
41
42
|
* ```typescript
|
|
@@ -52,6 +53,7 @@ export class ScanFeature extends Feature {
|
|
|
52
53
|
public readonly protocol: TScanProtocol;
|
|
53
54
|
|
|
54
55
|
// Protocol clients
|
|
56
|
+
private brotherClient: BrotherScanProtocol | null = null;
|
|
55
57
|
private esclClient: EsclProtocol | null = null;
|
|
56
58
|
private saneClient: SaneProtocol | null = null;
|
|
57
59
|
|
|
@@ -95,7 +97,11 @@ export class ScanFeature extends Feature {
|
|
|
95
97
|
// ============================================================================
|
|
96
98
|
|
|
97
99
|
protected async doConnect(): Promise<void> {
|
|
98
|
-
if (this.protocol === '
|
|
100
|
+
if (this.protocol === 'brother') {
|
|
101
|
+
this.brotherClient = new BrotherScanProtocol(this.address, this._port);
|
|
102
|
+
await this.brotherClient.connect();
|
|
103
|
+
this.updateCapabilitiesFromBrother(this.brotherClient.getCapabilities());
|
|
104
|
+
} else if (this.protocol === 'escl') {
|
|
99
105
|
this.esclClient = new EsclProtocol(this.address, this._port, this.isSecure);
|
|
100
106
|
// Fetch capabilities to verify connection
|
|
101
107
|
const caps = await this.esclClient.getCapabilities();
|
|
@@ -111,6 +117,10 @@ export class ScanFeature extends Feature {
|
|
|
111
117
|
}
|
|
112
118
|
|
|
113
119
|
protected async doDisconnect(): Promise<void> {
|
|
120
|
+
if (this.brotherClient) {
|
|
121
|
+
await this.brotherClient.disconnect();
|
|
122
|
+
this.brotherClient = null;
|
|
123
|
+
}
|
|
114
124
|
if (this.saneClient) {
|
|
115
125
|
try {
|
|
116
126
|
await this.saneClient.close();
|
|
@@ -153,7 +163,9 @@ export class ScanFeature extends Feature {
|
|
|
153
163
|
|
|
154
164
|
const opts = this.resolveOptions(options);
|
|
155
165
|
|
|
156
|
-
if (this.protocol === '
|
|
166
|
+
if (this.protocol === 'brother' && this.brotherClient) {
|
|
167
|
+
return this.scanWithBrother(opts);
|
|
168
|
+
} else if (this.protocol === 'escl' && this.esclClient) {
|
|
157
169
|
return this.scanWithEscl(opts);
|
|
158
170
|
} else if (this.protocol === 'sane' && this.saneClient) {
|
|
159
171
|
return this.scanWithSane(opts);
|
|
@@ -166,10 +178,13 @@ export class ScanFeature extends Feature {
|
|
|
166
178
|
* Cancel an ongoing scan
|
|
167
179
|
*/
|
|
168
180
|
public async cancelScan(): Promise<void> {
|
|
169
|
-
if (this.protocol === '
|
|
181
|
+
if (this.protocol === 'brother' && this.brotherClient) {
|
|
182
|
+
await this.brotherClient.cancel();
|
|
183
|
+
} else if (this.protocol === 'escl' && this.esclClient) {
|
|
184
|
+
await this.esclClient.cancelCurrentScan();
|
|
185
|
+
} else if (this.protocol === 'sane' && this.saneClient) {
|
|
170
186
|
await this.saneClient.cancel();
|
|
171
187
|
}
|
|
172
|
-
// eSCL cancellation is handled by deleting the job
|
|
173
188
|
this.emit('scan:cancelled');
|
|
174
189
|
}
|
|
175
190
|
|
|
@@ -177,6 +192,17 @@ export class ScanFeature extends Feature {
|
|
|
177
192
|
// Protocol-Specific Scanning
|
|
178
193
|
// ============================================================================
|
|
179
194
|
|
|
195
|
+
private async scanWithBrother(options: Required<IScanOptions>): Promise<IScanResult> {
|
|
196
|
+
if (!this.brotherClient) {
|
|
197
|
+
throw new Error('Brother scan client not initialized');
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
this.emit('scan:started', options);
|
|
201
|
+
const result = await this.brotherClient.scan(options);
|
|
202
|
+
this.emit('scan:completed', result);
|
|
203
|
+
return result;
|
|
204
|
+
}
|
|
205
|
+
|
|
180
206
|
private async scanWithEscl(options: Required<IScanOptions>): Promise<IScanResult> {
|
|
181
207
|
if (!this.esclClient) {
|
|
182
208
|
throw new Error('eSCL client not initialized');
|
|
@@ -229,7 +255,9 @@ export class ScanFeature extends Feature {
|
|
|
229
255
|
resolution: options?.resolution ?? 300,
|
|
230
256
|
format: options?.format ?? 'jpeg',
|
|
231
257
|
colorMode: options?.colorMode ?? 'color',
|
|
232
|
-
source: options?.source ??
|
|
258
|
+
source: options?.source ?? (
|
|
259
|
+
this.hasDuplex ? 'adf-duplex' : this.hasAdf ? 'adf' : 'flatbed'
|
|
260
|
+
),
|
|
233
261
|
area: options?.area ?? {
|
|
234
262
|
x: 0,
|
|
235
263
|
y: 0,
|
|
@@ -241,26 +269,43 @@ export class ScanFeature extends Feature {
|
|
|
241
269
|
};
|
|
242
270
|
}
|
|
243
271
|
|
|
244
|
-
private
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
272
|
+
private updateCapabilitiesFromBrother(caps: IScanCapabilities): void {
|
|
273
|
+
this.supportedResolutions = caps.resolutions;
|
|
274
|
+
this.supportedFormats = caps.formats;
|
|
275
|
+
this.supportedColorModes = caps.colorModes;
|
|
276
|
+
this.supportedSources = caps.sources;
|
|
277
|
+
this.hasAdf = caps.sources.includes('adf') || caps.sources.includes('adf-duplex');
|
|
278
|
+
this.hasDuplex = caps.sources.includes('adf-duplex');
|
|
279
|
+
this.maxWidth = caps.maxWidth;
|
|
280
|
+
this.maxHeight = caps.maxHeight;
|
|
281
|
+
this.minWidth = caps.minWidth;
|
|
282
|
+
this.minHeight = caps.minHeight;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
private updateCapabilitiesFromEscl(caps: IEsclCapabilities): void {
|
|
286
|
+
const sources = [caps.platen, caps.adf, caps.adfDuplex].filter((source) => source !== undefined);
|
|
287
|
+
if (sources.length > 0) {
|
|
288
|
+
this.supportedResolutions = [...new Set(
|
|
289
|
+
sources.flatMap((source) => source.supportedResolutions)
|
|
290
|
+
)].sort((left, right) => left - right);
|
|
291
|
+
this.maxWidth = Math.max(...sources.map((source) => source.maxWidth));
|
|
292
|
+
this.maxHeight = Math.max(...sources.map((source) => source.maxHeight));
|
|
293
|
+
this.minWidth = Math.min(...sources.map((source) => source.minWidth));
|
|
294
|
+
this.minHeight = Math.min(...sources.map((source) => source.minHeight));
|
|
263
295
|
}
|
|
296
|
+
|
|
297
|
+
const supportedSources: TScanSource[] = [];
|
|
298
|
+
if (caps.platen) supportedSources.push('flatbed');
|
|
299
|
+
if (caps.adf) supportedSources.push('adf');
|
|
300
|
+
if (caps.adfDuplex) supportedSources.push('adf-duplex');
|
|
301
|
+
if (supportedSources.length > 0) this.supportedSources = supportedSources;
|
|
302
|
+
this.hasAdf = Boolean(caps.adf || caps.adfDuplex);
|
|
303
|
+
this.hasDuplex = Boolean(caps.adfDuplex);
|
|
304
|
+
|
|
305
|
+
const formats = mapDocumentFormats(sources.flatMap((source) => source.documentFormats));
|
|
306
|
+
const colorModes = mapColorModes(sources.flatMap((source) => source.colorModes));
|
|
307
|
+
if (formats.length > 0) this.supportedFormats = formats;
|
|
308
|
+
if (colorModes.length > 0) this.supportedColorModes = colorModes;
|
|
264
309
|
}
|
|
265
310
|
|
|
266
311
|
private colorModeToSane(mode: TColorMode): string {
|
|
@@ -329,6 +374,27 @@ export class ScanFeature extends Feature {
|
|
|
329
374
|
}
|
|
330
375
|
}
|
|
331
376
|
|
|
377
|
+
function mapDocumentFormats(formats: string[]): TScanFormat[] {
|
|
378
|
+
const mapped = formats.flatMap((format): TScanFormat[] => {
|
|
379
|
+
if (format === 'application/pdf') return ['pdf'];
|
|
380
|
+
if (format === 'image/jpeg') return ['jpeg'];
|
|
381
|
+
if (format === 'image/png') return ['png'];
|
|
382
|
+
if (format === 'image/tiff') return ['tiff'];
|
|
383
|
+
return [];
|
|
384
|
+
});
|
|
385
|
+
return [...new Set(mapped)];
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function mapColorModes(modes: string[]): TColorMode[] {
|
|
389
|
+
const mapped = modes.flatMap((mode): TColorMode[] => {
|
|
390
|
+
if (mode.startsWith('RGB')) return ['color'];
|
|
391
|
+
if (mode.startsWith('Grayscale')) return ['grayscale'];
|
|
392
|
+
if (mode.startsWith('BlackAndWhite')) return ['blackwhite'];
|
|
393
|
+
return [];
|
|
394
|
+
});
|
|
395
|
+
return [...new Set(mapped)];
|
|
396
|
+
}
|
|
397
|
+
|
|
332
398
|
// ============================================================================
|
|
333
399
|
// Parsing Helpers
|
|
334
400
|
// ============================================================================
|
package/ts/index.ts
CHANGED
|
@@ -17,6 +17,19 @@ export {
|
|
|
17
17
|
SSDP_SERVICE_TYPES,
|
|
18
18
|
} from './devicemanager.classes.devicemanager.js';
|
|
19
19
|
|
|
20
|
+
export {
|
|
21
|
+
ObservableEventEmitter,
|
|
22
|
+
type IObservableEvent,
|
|
23
|
+
} from './events/index.js';
|
|
24
|
+
|
|
25
|
+
export {
|
|
26
|
+
SmbShareProvider,
|
|
27
|
+
type ISmbDocumentEvent,
|
|
28
|
+
type ISmbServerAdapter,
|
|
29
|
+
type ISmbShareProviderDependencies,
|
|
30
|
+
type ISmbShareProviderOptions,
|
|
31
|
+
} from './providers/index.js';
|
|
32
|
+
|
|
20
33
|
// ============================================================================
|
|
21
34
|
// Universal Device & Features
|
|
22
35
|
// ============================================================================
|
|
@@ -99,6 +112,9 @@ export {
|
|
|
99
112
|
|
|
100
113
|
export {
|
|
101
114
|
EsclProtocol,
|
|
115
|
+
EsclScanError,
|
|
116
|
+
BrotherScanProtocol,
|
|
117
|
+
BrotherScanError,
|
|
102
118
|
SaneProtocol,
|
|
103
119
|
IppProtocol,
|
|
104
120
|
SnmpProtocol,
|
|
@@ -114,6 +130,10 @@ export {
|
|
|
114
130
|
// Home Assistant protocol
|
|
115
131
|
HomeAssistantProtocol,
|
|
116
132
|
type ISnmpOptions,
|
|
133
|
+
type IBrotherScanLease,
|
|
134
|
+
type IBrotherScanOptions,
|
|
135
|
+
type IBrotherScanProtocolOptions,
|
|
136
|
+
type TBrotherScanErrorCode,
|
|
117
137
|
type ISnmpVarbind,
|
|
118
138
|
type TSnmpValueType,
|
|
119
139
|
type TNutStatusFlag,
|
|
@@ -160,6 +180,7 @@ export * from './interfaces/index.js';
|
|
|
160
180
|
export type {
|
|
161
181
|
ISsdpDevice,
|
|
162
182
|
ISsdpDeviceDescription,
|
|
183
|
+
ISsdpDiscoveryDependencies,
|
|
163
184
|
ISsdpService,
|
|
164
185
|
ISsdpIcon,
|
|
165
186
|
} from './discovery/discovery.classes.ssdp.js';
|
|
@@ -79,7 +79,7 @@ export interface IFeatureInfo {
|
|
|
79
79
|
// Scan Feature
|
|
80
80
|
// ============================================================================
|
|
81
81
|
|
|
82
|
-
export type TScanProtocol = 'escl' | 'sane' | 'wia';
|
|
82
|
+
export type TScanProtocol = 'brother' | 'escl' | 'sane' | 'wia';
|
|
83
83
|
export type TScanFormat = 'png' | 'jpeg' | 'pdf' | 'tiff';
|
|
84
84
|
export type TColorMode = 'color' | 'grayscale' | 'blackwhite';
|
|
85
85
|
export type TScanSource = 'flatbed' | 'adf' | 'adf-duplex';
|
|
@@ -120,6 +120,23 @@ export interface IScanResult {
|
|
|
120
120
|
resolution: number;
|
|
121
121
|
colorMode: TColorMode;
|
|
122
122
|
mimeType: string;
|
|
123
|
+
/** Number of documents returned by a multipage-capable scanner transport */
|
|
124
|
+
pageCount?: number;
|
|
125
|
+
/** All returned documents, including the first document exposed as data */
|
|
126
|
+
pages?: IScanPage[];
|
|
127
|
+
/** Whether the scanner reported a cleanly completed job */
|
|
128
|
+
isComplete?: boolean;
|
|
129
|
+
/** Scanner reason when a partial result is returned with an error */
|
|
130
|
+
jobStateReason?: string;
|
|
131
|
+
/** Differences detected between requested and returned scan settings */
|
|
132
|
+
settingsWarnings?: string[];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export interface IScanPage {
|
|
136
|
+
data: Buffer;
|
|
137
|
+
width: number;
|
|
138
|
+
height: number;
|
|
139
|
+
mimeType: string;
|
|
123
140
|
}
|
|
124
141
|
|
|
125
142
|
export interface IScanFeatureInfo extends IFeatureInfo {
|
|
@@ -143,8 +160,10 @@ export type TPrintQuality = 'draft' | 'normal' | 'high';
|
|
|
143
160
|
export type TPrintColorMode = 'color' | 'monochrome';
|
|
144
161
|
|
|
145
162
|
export interface IPrintCapabilities {
|
|
163
|
+
airPrintSupported: boolean;
|
|
146
164
|
colorSupported: boolean;
|
|
147
165
|
duplexSupported: boolean;
|
|
166
|
+
documentFormats: string[];
|
|
148
167
|
mediaSizes: string[];
|
|
149
168
|
mediaTypes: string[];
|
|
150
169
|
resolutions: number[];
|
|
@@ -168,7 +187,7 @@ export interface IPrintJob {
|
|
|
168
187
|
name: string;
|
|
169
188
|
state: 'pending' | 'processing' | 'completed' | 'canceled' | 'aborted';
|
|
170
189
|
stateReason?: string;
|
|
171
|
-
createdAt
|
|
190
|
+
createdAt?: Date;
|
|
172
191
|
completedAt?: Date;
|
|
173
192
|
pagesPrinted?: number;
|
|
174
193
|
pagesTotal?: number;
|
|
@@ -177,8 +196,10 @@ export interface IPrintJob {
|
|
|
177
196
|
export interface IPrintFeatureInfo extends IFeatureInfo {
|
|
178
197
|
type: 'print';
|
|
179
198
|
protocol: TPrintProtocol;
|
|
199
|
+
airPrintSupported: boolean;
|
|
180
200
|
supportsColor: boolean;
|
|
181
201
|
supportsDuplex: boolean;
|
|
202
|
+
supportedDocumentFormats: string[];
|
|
182
203
|
supportedMediaSizes: string[];
|
|
183
204
|
}
|
|
184
205
|
|
package/ts/interfaces/index.ts
CHANGED
|
@@ -14,7 +14,7 @@ export type TConnectionState = 'disconnected' | 'connecting' | 'connected' | 'er
|
|
|
14
14
|
// Scanner Types
|
|
15
15
|
// ============================================================================
|
|
16
16
|
|
|
17
|
-
export type TScannerProtocol = 'sane' | 'escl';
|
|
17
|
+
export type TScannerProtocol = 'brother' | 'sane' | 'escl';
|
|
18
18
|
export type TScanFormat = 'png' | 'jpeg' | 'pdf' | 'tiff';
|
|
19
19
|
export type TColorMode = 'color' | 'grayscale' | 'blackwhite';
|
|
20
20
|
export type TScanSource = 'flatbed' | 'adf' | 'adf-duplex';
|
|
@@ -105,6 +105,23 @@ export interface IScanResult {
|
|
|
105
105
|
colorMode: TColorMode;
|
|
106
106
|
/** MIME type */
|
|
107
107
|
mimeType: string;
|
|
108
|
+
/** Number of documents returned by a multipage-capable scanner transport */
|
|
109
|
+
pageCount?: number;
|
|
110
|
+
/** All returned documents, including the first document exposed as data */
|
|
111
|
+
pages?: IScanPage[];
|
|
112
|
+
/** Whether the scanner reported a cleanly completed job */
|
|
113
|
+
isComplete?: boolean;
|
|
114
|
+
/** Scanner reason when a partial result is returned with an error */
|
|
115
|
+
jobStateReason?: string;
|
|
116
|
+
/** Differences detected between requested and returned scan settings */
|
|
117
|
+
settingsWarnings?: string[];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface IScanPage {
|
|
121
|
+
data: Buffer;
|
|
122
|
+
width: number;
|
|
123
|
+
height: number;
|
|
124
|
+
mimeType: string;
|
|
108
125
|
}
|
|
109
126
|
|
|
110
127
|
export interface IScannerCapabilities {
|
|
@@ -154,7 +171,7 @@ export interface IPrintJob {
|
|
|
154
171
|
name: string;
|
|
155
172
|
state: 'pending' | 'processing' | 'completed' | 'canceled' | 'aborted';
|
|
156
173
|
stateReason?: string;
|
|
157
|
-
createdAt
|
|
174
|
+
createdAt?: Date;
|
|
158
175
|
completedAt?: Date;
|
|
159
176
|
pagesPrinted?: number;
|
|
160
177
|
pagesTotal?: number;
|
|
@@ -321,8 +338,10 @@ export interface INetworkScanOptions {
|
|
|
321
338
|
concurrency?: number;
|
|
322
339
|
/** Timeout per probe in milliseconds (default: 2000) */
|
|
323
340
|
timeout?: number;
|
|
324
|
-
/** Ports to probe (
|
|
341
|
+
/** Ports to probe (includes Brother native scan on 54921 by default) */
|
|
325
342
|
ports?: number[];
|
|
343
|
+
/** Check for Brother native network scanners (default: true) */
|
|
344
|
+
probeBrother?: boolean;
|
|
326
345
|
/** Check for eSCL scanners (default: true) */
|
|
327
346
|
probeEscl?: boolean;
|
|
328
347
|
/** Check for IPP printers (default: true) */
|
|
@@ -339,7 +358,7 @@ export interface INetworkScanOptions {
|
|
|
339
358
|
|
|
340
359
|
export interface INetworkScanDevice {
|
|
341
360
|
type: 'scanner' | 'printer' | 'speaker';
|
|
342
|
-
protocol: 'escl' | 'sane' | 'ipp' | 'jetdirect' | 'airplay' | 'sonos' | 'chromecast';
|
|
361
|
+
protocol: 'brother' | 'escl' | 'sane' | 'ipp' | 'jetdirect' | 'airplay' | 'sonos' | 'chromecast';
|
|
343
362
|
port: number;
|
|
344
363
|
name?: string;
|
|
345
364
|
model?: string;
|
package/ts/plugins.ts
CHANGED
|
@@ -1,41 +1,22 @@
|
|
|
1
1
|
// native scope
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import * as net from 'net';
|
|
4
|
-
import * as dgram from 'dgram';
|
|
4
|
+
import * as dgram from 'node:dgram';
|
|
5
|
+
import * as os from 'node:os';
|
|
5
6
|
import * as events from 'events';
|
|
6
7
|
|
|
7
|
-
export { path, net, dgram, events };
|
|
8
|
+
export { path, net, dgram, os, events };
|
|
8
9
|
|
|
9
10
|
// @push.rocks scope
|
|
10
11
|
import * as smartpath from '@push.rocks/smartpath';
|
|
11
|
-
import * as smartpromise from '@push.rocks/smartpromise';
|
|
12
12
|
import * as smartdelay from '@push.rocks/smartdelay';
|
|
13
|
-
import * as smartnetwork from '@push.rocks/smartnetwork';
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
import { SmartRequest } from '@push.rocks/smartrequest';
|
|
17
|
-
export { SmartRequest };
|
|
18
|
-
|
|
19
|
-
export {
|
|
20
|
-
smartpath,
|
|
21
|
-
smartpromise,
|
|
22
|
-
smartdelay,
|
|
23
|
-
smartnetwork,
|
|
24
|
-
};
|
|
14
|
+
export { smartpath, smartdelay };
|
|
25
15
|
|
|
26
16
|
// third party
|
|
27
17
|
import * as bonjourService from 'bonjour-service';
|
|
28
|
-
import ipp from 'ipp';
|
|
29
|
-
import nodeSsdpModule from 'node-ssdp';
|
|
30
18
|
import * as netSnmp from 'net-snmp';
|
|
31
|
-
import * as sonos from 'sonos';
|
|
32
|
-
import * as castv2Client from 'castv2-client';
|
|
33
19
|
import WebSocket from 'ws';
|
|
20
|
+
import * as rxjs from 'rxjs';
|
|
34
21
|
|
|
35
|
-
|
|
36
|
-
const nodeSsdp = {
|
|
37
|
-
Client: nodeSsdpModule.Client,
|
|
38
|
-
Server: nodeSsdpModule.Server,
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
export { bonjourService, ipp, nodeSsdp, netSnmp, sonos, castv2Client, WebSocket };
|
|
22
|
+
export { bonjourService, netSnmp, WebSocket, rxjs };
|
package/ts/protocols/index.ts
CHANGED
|
@@ -4,7 +4,17 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
// eSCL/AirScan scanner protocol
|
|
7
|
-
export { EsclProtocol } from './protocol.escl.js';
|
|
7
|
+
export { EsclProtocol, EsclScanError } from './protocol.escl.js';
|
|
8
|
+
|
|
9
|
+
// Brother native network scanner protocol
|
|
10
|
+
export {
|
|
11
|
+
BrotherScanProtocol,
|
|
12
|
+
BrotherScanError,
|
|
13
|
+
type IBrotherScanLease,
|
|
14
|
+
type IBrotherScanOptions,
|
|
15
|
+
type IBrotherScanProtocolOptions,
|
|
16
|
+
type TBrotherScanErrorCode,
|
|
17
|
+
} from './protocol.brother.js';
|
|
8
18
|
|
|
9
19
|
// SANE network scanner protocol
|
|
10
20
|
export { SaneProtocol } from './protocol.sane.js';
|