node-hp-scan-to 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +92 -11
  2. package/dist/Destination.d.ts +8 -0
  3. package/dist/Destination.js +116 -0
  4. package/dist/Destination.js.map +1 -0
  5. package/dist/Event.d.ts +21 -0
  6. package/dist/Event.js +47 -0
  7. package/dist/Event.js.map +1 -0
  8. package/dist/EventTable.d.ts +11 -0
  9. package/dist/EventTable.js +27 -0
  10. package/dist/EventTable.js.map +1 -0
  11. package/dist/HPApi.d.ts +42 -0
  12. package/dist/HPApi.js +529 -0
  13. package/dist/HPApi.js.map +1 -0
  14. package/dist/Job.d.ts +36 -0
  15. package/dist/Job.js +95 -0
  16. package/dist/Job.js.map +1 -0
  17. package/dist/JpegUtil.d.ts +26 -0
  18. package/dist/JpegUtil.js +238 -0
  19. package/dist/JpegUtil.js.map +1 -0
  20. package/dist/PathHelper.d.ts +5 -0
  21. package/dist/PathHelper.js +79 -0
  22. package/dist/PathHelper.js.map +1 -0
  23. package/dist/ScanContent.d.ts +12 -0
  24. package/dist/ScanContent.js +82 -0
  25. package/dist/ScanContent.js.map +1 -0
  26. package/dist/ScanJobSettings.d.ts +6 -0
  27. package/dist/ScanJobSettings.js +96 -0
  28. package/dist/ScanJobSettings.js.map +1 -0
  29. package/dist/ScanStatus.d.ts +18 -0
  30. package/dist/ScanStatus.js +36 -0
  31. package/dist/ScanStatus.js.map +1 -0
  32. package/dist/WalkupScanDestination.d.ts +24 -0
  33. package/dist/WalkupScanDestination.js +41 -0
  34. package/dist/WalkupScanDestination.js.map +1 -0
  35. package/dist/WalkupScanDestinations.d.ts +11 -0
  36. package/dist/WalkupScanDestinations.js +27 -0
  37. package/dist/WalkupScanDestinations.js.map +1 -0
  38. package/dist/WalkupScanToCompDestination.d.ts +26 -0
  39. package/dist/WalkupScanToCompDestination.js +44 -0
  40. package/dist/WalkupScanToCompDestination.js.map +1 -0
  41. package/dist/WalkupScanToCompDestinations.d.ts +11 -0
  42. package/dist/WalkupScanToCompDestinations.js +27 -0
  43. package/dist/WalkupScanToCompDestinations.js.map +1 -0
  44. package/dist/WalkupScanToCompEvent.d.ts +10 -0
  45. package/dist/WalkupScanToCompEvent.js +17 -0
  46. package/dist/WalkupScanToCompEvent.js.map +1 -0
  47. package/dist/index.d.ts +2 -0
  48. package/dist/index.js +630 -292
  49. package/dist/index.js.map +1 -0
  50. package/nodemon.json +5 -5
  51. package/package.json +72 -51
  52. package/src/Destination.ts +63 -45
  53. package/src/Event.ts +45 -31
  54. package/src/EventTable.ts +25 -25
  55. package/src/HPApi.ts +365 -222
  56. package/src/Job.ts +100 -62
  57. package/src/JpegUtil.ts +319 -0
  58. package/src/PathHelper.ts +44 -0
  59. package/src/ScanContent.ts +34 -0
  60. package/src/ScanJobSettings.ts +55 -55
  61. package/src/ScanStatus.ts +36 -31
  62. package/src/WalkupScanDestination.ts +46 -44
  63. package/src/WalkupScanDestinations.ts +29 -27
  64. package/src/WalkupScanToCompDestination.ts +55 -0
  65. package/src/WalkupScanToCompDestinations.ts +34 -0
  66. package/src/WalkupScanToCompEvent.ts +18 -0
  67. package/src/index.ts +555 -205
  68. package/tsconfig.json +33 -33
  69. package/.travis.yml +0 -13
  70. package/protocol_doc/index.md +0 -1484
  71. package/test/dummy.js +0 -0
package/src/index.ts CHANGED
@@ -1,205 +1,555 @@
1
- #!/usr/bin/env node
2
- "use strict";
3
-
4
- import os from "os";
5
- import fs from "fs";
6
- import path from "path";
7
- import util from "util";
8
-
9
- import Bonjour, { Service } from "bonjour";
10
-
11
- import Destination from "./Destination";
12
- import ScanJobSettings from "./ScanJobSettings";
13
- import Event from "./Event";
14
- import HPApi from "./HPApi";
15
- import Job from "./Job";
16
-
17
- function delay(t: number) {
18
- return new Promise(function(resolve) {
19
- setTimeout(resolve, t);
20
- });
21
- }
22
-
23
- async function waitForScanEvent(resourceURI: string): Promise<Event> {
24
- console.log("Start listening for new ScanEvent");
25
-
26
- let eventTable = await HPApi.getEvents();
27
- let acceptedScanEvent = null;
28
- let currentEtag = eventTable.etag;
29
- while (acceptedScanEvent == null) {
30
- eventTable = await HPApi.getEvents(currentEtag, 1200);
31
- currentEtag = eventTable.etag;
32
-
33
- acceptedScanEvent = eventTable.eventTable.events.find(
34
- ev => ev.isScanEvent && ev.resourceURI === resourceURI
35
- );
36
- }
37
- return acceptedScanEvent;
38
- }
39
-
40
- async function waitPrinterUntilItIsReadyToUploadOrCompleted(
41
- jobUrl: string
42
- ): Promise<Job> {
43
- let job = null;
44
- let isReadyToUpload = false;
45
- do {
46
- job = await HPApi.getJob(jobUrl);
47
- isReadyToUpload =
48
- job.pageState === "ReadyToUpload" || job.jobState === "Completed";
49
- await delay(200);
50
- } while (!isReadyToUpload);
51
- return job;
52
- }
53
-
54
- async function register() {
55
- const walkupScanDestinations = await HPApi.getWalkupScanDestinations();
56
- const hostname = os.hostname();
57
-
58
- let destinations = walkupScanDestinations.destinations;
59
-
60
- console.log(
61
- "Host destinations fetched:",
62
- destinations.map(d => d.name).join(", ")
63
- );
64
-
65
- let destination = destinations.find(x => x.name === hostname);
66
- let resourceURI;
67
- if (destination) {
68
- console.log(
69
- `Re-using existing destination: ${hostname} - ${destination.resourceURI}`
70
- );
71
- resourceURI = destination.resourceURI;
72
- } else {
73
- resourceURI = await HPApi.registerDestination(
74
- new Destination(hostname, hostname)
75
- );
76
- console.log(`New Destination registered: ${hostname} - ${resourceURI}`);
77
- }
78
- return resourceURI;
79
- }
80
-
81
- async function getNextFile(
82
- folder: string,
83
- currentPageNumber: string
84
- ): Promise<string> {
85
- return path.join(folder, `scanPage${currentPageNumber}.jpg`);
86
- }
87
-
88
- async function saveScan(event: Event) {
89
- const destination = await HPApi.getDestination(event.resourceURI);
90
-
91
- const folder = await util.promisify(fs.mkdtemp)(
92
- path.join(os.tmpdir(), "scan-to-pc")
93
- );
94
- console.log(`Target folder: ${folder}`);
95
-
96
- console.log("Selected shortcut: " + destination.shortcut);
97
- const scanStatus = await HPApi.getScanStatus();
98
- console.log("Afd is : " + scanStatus.adfState);
99
-
100
- let inputSource = scanStatus.getInputSource();
101
- let contentType = destination.getContentType();
102
-
103
- let scanJobSettings = new ScanJobSettings(inputSource, contentType);
104
- const jobUrl = await HPApi.postJob(scanJobSettings);
105
-
106
- console.log("New job created:", jobUrl);
107
-
108
- let job = await HPApi.getJob(jobUrl);
109
- while (job.jobState !== "Completed") {
110
- job = await waitPrinterUntilItIsReadyToUploadOrCompleted(jobUrl);
111
-
112
- if (job.jobState == "Completed") {
113
- continue;
114
- }
115
-
116
- if (job.jobState === "Processing") {
117
- if (
118
- job.pageState == "ReadyToUpload" &&
119
- job.binaryURL != null &&
120
- job.currentPageNumber != null
121
- ) {
122
- console.log(
123
- `Ready to download page ${job.currentPageNumber} at:`,
124
- job.binaryURL
125
- );
126
-
127
- const filePath = await HPApi.downloadPage(
128
- job.binaryURL,
129
- await getNextFile(folder, job.currentPageNumber)
130
- );
131
- console.log("Page downloaded to:", filePath);
132
- } else {
133
- console.log(`Unknown pageState: ${job.pageState}`);
134
- await delay(200);
135
- }
136
- } else {
137
- console.log(`Unknown jobState: ${job.jobState}`);
138
- await delay(200);
139
- }
140
- }
141
- console.log(`Job state: ${job.jobState}, totalPages: ${job.totalPageNumber}`);
142
- }
143
-
144
- async function init() {
145
- let keepActive = true;
146
- let errorCount = 0;
147
- while (keepActive) {
148
- try {
149
- let resourceURI = await register();
150
-
151
- console.log("Waiting scan event for:", resourceURI);
152
- const event = await waitForScanEvent(resourceURI);
153
- console.log("Scan event captured");
154
- await saveScan(event);
155
- } catch (e) {
156
- errorCount++;
157
- console.error(e);
158
- }
159
-
160
- if (errorCount === 50) {
161
- keepActive = false;
162
- }
163
-
164
- await delay(1000);
165
- }
166
- }
167
-
168
- interface OfficeJetBonjourService extends Service {
169
- addresses?: string[];
170
- }
171
-
172
- function findOfficejetIp(): Promise<string> {
173
- return new Promise(resolve => {
174
- const bonjour = Bonjour();
175
- console.log("Searching printer...");
176
- let browser = bonjour.find(
177
- {
178
- type: "http"
179
- },
180
- (service: OfficeJetBonjourService) => {
181
- console.log(".");
182
- if (
183
- service.name.startsWith("Officejet 6500 E710n-z") &&
184
- service.port === 80 &&
185
- service.type === "http" &&
186
- service.addresses != null
187
- ) {
188
- browser.stop();
189
- bonjour.destroy();
190
- console.log(`Found: ${service.name}`);
191
- resolve(service.addresses[0]);
192
- }
193
- }
194
- );
195
- browser.start();
196
- });
197
- }
198
-
199
- async function main() {
200
- const ip = await findOfficejetIp();
201
- HPApi.setPrinterIP(ip);
202
- await init();
203
- }
204
-
205
- main();
1
+ #!/usr/bin/env node
2
+ // noinspection XmlDeprecatedElement,HtmlDeprecatedTag
3
+
4
+ "use strict";
5
+
6
+ import os from "os";
7
+ import fs from "fs/promises";
8
+ import { Command } from "commander";
9
+ import Bonjour from "bonjour";
10
+
11
+ import Destination from "./Destination";
12
+ import ScanJobSettings from "./ScanJobSettings";
13
+ import Event from "./Event";
14
+ import HPApi from "./HPApi";
15
+ import Job from "./Job";
16
+ import WalkupScanDestination from "./WalkupScanDestination";
17
+ import WalkupScanToCompDestination from "./WalkupScanToCompDestination";
18
+ import JpegUtil from "../src/JpegUtil";
19
+ import PathHelper from "./PathHelper";
20
+ import { createPdfFrom, ScanContent, ScanPage } from "./ScanContent";
21
+
22
+ const program = new Command();
23
+
24
+ function delay(t: number): Promise<void> {
25
+ return new Promise(function (resolve) {
26
+ setTimeout(resolve, t);
27
+ });
28
+ }
29
+
30
+ async function waitForScanEvent(
31
+ resourceURI: string,
32
+ afterEtag: string | null = null
33
+ ): Promise<Event> {
34
+ console.log("Start listening for new ScanEvent");
35
+
36
+ let eventTable = await HPApi.getEvents(afterEtag ?? "");
37
+ let acceptedScanEvent = null;
38
+ let currentEtag = eventTable.etag;
39
+ while (acceptedScanEvent == null) {
40
+ eventTable = await HPApi.getEvents(currentEtag, 1200);
41
+ currentEtag = eventTable.etag;
42
+
43
+ acceptedScanEvent = eventTable.eventTable.events.find(
44
+ (ev) =>
45
+ ev.isScanEvent &&
46
+ ev.destinationURI &&
47
+ ev.destinationURI.indexOf(resourceURI) >= 0
48
+ );
49
+ }
50
+ return acceptedScanEvent;
51
+ }
52
+
53
+ async function waitPrinterUntilItIsReadyToUploadOrCompleted(
54
+ jobUrl: string
55
+ ): Promise<Job> {
56
+ let job = null;
57
+ let isReadyToUpload = false;
58
+ do {
59
+ job = await HPApi.getJob(jobUrl);
60
+ if (job.jobState === "Canceled") {
61
+ return job;
62
+ } else if (
63
+ job.pageState === "ReadyToUpload" ||
64
+ job.jobState === "Completed"
65
+ ) {
66
+ isReadyToUpload = true;
67
+ } else if (job.jobState == "Processing") {
68
+ isReadyToUpload = false;
69
+ } else {
70
+ console.log(`Unknown jobState: ${job.jobState}`);
71
+ }
72
+ await delay(300);
73
+ } while (!isReadyToUpload);
74
+ return job;
75
+ }
76
+
77
+ async function register(): Promise<string> {
78
+ let destination;
79
+ const hostname = os.hostname();
80
+ const toComp = await HPApi.getWalkupScanToCompCaps();
81
+
82
+ if (toComp) {
83
+ const walkupScanDestinations =
84
+ await HPApi.getWalkupScanToCompDestinations();
85
+ const destinations = walkupScanDestinations.destinations;
86
+
87
+ console.log(
88
+ "Host destinations fetched:",
89
+ destinations.map((d) => d.name).join(", ")
90
+ );
91
+
92
+ destination = destinations.find((x) => x.name === hostname);
93
+ } else {
94
+ const walkupScanDestinations = await HPApi.getWalkupScanDestinations();
95
+ const destinations = walkupScanDestinations.destinations;
96
+
97
+ console.log(
98
+ "Host destinations fetched:",
99
+ destinations.map((d) => d.name).join(", ")
100
+ );
101
+
102
+ destination = destinations.find((x) => x.name === hostname);
103
+ }
104
+
105
+ let resourceURI;
106
+ if (destination) {
107
+ console.log(
108
+ `Re-using existing destination: ${hostname} - ${destination.resourceURI}`
109
+ );
110
+ resourceURI = destination.resourceURI;
111
+ } else {
112
+ resourceURI = await HPApi.registerDestination(
113
+ new Destination(hostname, hostname, toComp),
114
+ toComp
115
+ );
116
+ console.log(`New Destination registered: ${hostname} - ${resourceURI}`);
117
+ }
118
+ return resourceURI;
119
+ }
120
+
121
+ async function TryGetDestination(event: Event) {
122
+ //this code can in some cases be executed before the user actually chooses between Document or Photo
123
+ //so lets fetch the contentType (Document or Photo) until we get a value
124
+ let destination: WalkupScanDestination | WalkupScanToCompDestination | null =
125
+ null;
126
+
127
+ for (let i = 0; i < 20; i++) {
128
+ const destinationURI = event.destinationURI;
129
+ if (destinationURI) {
130
+ destination = await HPApi.getDestination(destinationURI);
131
+
132
+ const shortcut = destination.shortcut;
133
+ if (shortcut !== "") {
134
+ return destination;
135
+ }
136
+ } else {
137
+ console.log(`No destination URI found`);
138
+ }
139
+
140
+ console.log(`No shortcut yet available, attempt: ${i + 1}/20`);
141
+ await new Promise((resolve) => setTimeout(resolve, 1000)); //wait 1s
142
+ }
143
+
144
+ console.log("Failing to detect destination shortcut");
145
+ console.log(JSON.stringify(destination));
146
+ return null;
147
+ }
148
+
149
+ async function fixJpegSize(filePath: string): Promise<number | null> {
150
+ const buffer: Buffer = await fs.readFile(filePath);
151
+
152
+ let height = JpegUtil.fixSizeWithDNL(buffer);
153
+ if (height != null) {
154
+ // rewrite the fixed file
155
+ await fs.writeFile(filePath, buffer);
156
+ return height;
157
+ }
158
+ return null;
159
+ }
160
+
161
+ function createScanPage(
162
+ job: Job,
163
+ currentPageNumber: number,
164
+ filePath: string,
165
+ sizeFixed: number | null
166
+ ): ScanPage {
167
+ let height = sizeFixed ?? job.imageHeight;
168
+ return {
169
+ path: filePath,
170
+ pageNumber: currentPageNumber,
171
+ width: job.imageWidth,
172
+ height,
173
+ xResolution: job.xResolution,
174
+ yResolution: job.yResolution,
175
+ };
176
+ }
177
+
178
+ async function handleProcessingState(
179
+ job: Job,
180
+ inputSource: "Adf" | "Platen",
181
+ folder: string,
182
+ scanCount: number,
183
+ currentPageNumber: number
184
+ ): Promise<ScanPage | null> {
185
+ if (
186
+ job.pageState == "ReadyToUpload" &&
187
+ job.binaryURL != null &&
188
+ job.currentPageNumber != null
189
+ ) {
190
+ console.log(
191
+ `Ready to download page job page ${job.currentPageNumber} at:`,
192
+ job.binaryURL
193
+ );
194
+
195
+ const destinationFilePath = PathHelper.getFileForPage(
196
+ folder,
197
+ scanCount,
198
+ currentPageNumber,
199
+ program.opts().pattern,
200
+ "jpg"
201
+ );
202
+ const filePath = await HPApi.downloadPage(
203
+ job.binaryURL,
204
+ destinationFilePath
205
+ );
206
+ console.log("Page downloaded to:", filePath);
207
+
208
+ let sizeFixed: null | number = null;
209
+ if (inputSource == "Adf") {
210
+ sizeFixed = await fixJpegSize(filePath);
211
+ if (sizeFixed == null) {
212
+ console.log(
213
+ `File size has not been fixed, DNF may not have been found and approximate height is: ${job.imageHeight}`
214
+ );
215
+ }
216
+ }
217
+ return createScanPage(job, currentPageNumber, filePath, sizeFixed);
218
+ } else {
219
+ console.log(`Unknown pageState: ${job.pageState}`);
220
+ await delay(200);
221
+ return null;
222
+ }
223
+ }
224
+
225
+ async function waitScanRequest(compEventURI: string): Promise<boolean> {
226
+ const waitMax = 50;
227
+ for (let i = 0; i < waitMax; i++) {
228
+ let walkupScanToCompEvent = await HPApi.getWalkupScanToCompEvent(
229
+ compEventURI
230
+ );
231
+ let message = walkupScanToCompEvent.eventType;
232
+ if (message === "HostSelected") {
233
+ // this ok to wait
234
+ } else if (message === "ScanRequested") {
235
+ break;
236
+ } else if (message === "ScanNewPageRequested") {
237
+ break;
238
+ } else if (message === "ScanPagesComplete") {
239
+ console.log("no more page to scan, scan is finished");
240
+ return false;
241
+ } else {
242
+ console.log(`Unknown eventType: ${message}`);
243
+ return false;
244
+ }
245
+
246
+ console.log(`Waiting user input: ${i + 1}/${waitMax}`);
247
+ await new Promise((resolve) => setTimeout(resolve, 1000)); //wait 1s
248
+ }
249
+ return true;
250
+ }
251
+
252
+ async function executeScanJob(
253
+ scanJobSettings: ScanJobSettings,
254
+ inputSource: "Adf" | "Platen",
255
+ folder: string,
256
+ scanCount: number,
257
+ scanJobContent: ScanContent
258
+ ) {
259
+ const jobUrl = await HPApi.postJob(scanJobSettings);
260
+
261
+ console.log("New job created:", jobUrl);
262
+
263
+ let job = await HPApi.getJob(jobUrl);
264
+ while (job.jobState !== "Completed") {
265
+ job = await waitPrinterUntilItIsReadyToUploadOrCompleted(jobUrl);
266
+
267
+ if (job.jobState == "Completed") {
268
+ continue;
269
+ }
270
+
271
+ if (job.jobState === "Processing") {
272
+ const page = await handleProcessingState(
273
+ job,
274
+ inputSource,
275
+ folder,
276
+ scanCount,
277
+ scanJobContent.elements.length + 1
278
+ );
279
+ if (page != null) {
280
+ scanJobContent.elements.push(page);
281
+ }
282
+ } else if (job.jobState === "Canceled") {
283
+ console.log("Job cancelled by device");
284
+ break;
285
+ } else {
286
+ console.log(`Unhandled jobState: ${job.jobState}`);
287
+ await delay(200);
288
+ }
289
+ }
290
+ console.log(
291
+ `Job state: ${job.jobState}, totalPages: ${job.totalPageNumber}:`
292
+ );
293
+ }
294
+
295
+ async function waitScanNewPageRequest(compEventURI: string): Promise<boolean> {
296
+ let startNewScanJob = false;
297
+ let wait = true;
298
+ while (wait) {
299
+ await new Promise((resolve) => setTimeout(resolve, 1000)); //wait 1s
300
+
301
+ let walkupScanToCompEvent = await HPApi.getWalkupScanToCompEvent(
302
+ compEventURI
303
+ );
304
+ let message = walkupScanToCompEvent.eventType;
305
+
306
+ if (message === "ScanNewPageRequested") {
307
+ startNewScanJob = true;
308
+ wait = false;
309
+ } else if (message === "ScanPagesComplete") {
310
+ wait = false;
311
+ } else if (message === "ScanRequested") {
312
+ // continue waiting
313
+ } else {
314
+ wait = false;
315
+ console.log(`Unknown eventType: ${message}`);
316
+ }
317
+ }
318
+ return startNewScanJob;
319
+ }
320
+
321
+ async function executeScanJobs(
322
+ scanJobSettings: ScanJobSettings,
323
+ inputSource: "Adf" | "Platen",
324
+ folder: string,
325
+ scanCount: number,
326
+ scanJobContent: ScanContent,
327
+ firstEvent: Event
328
+ ) {
329
+ await executeScanJob(
330
+ scanJobSettings,
331
+ inputSource,
332
+ folder,
333
+ scanCount,
334
+ scanJobContent
335
+ );
336
+ let lastEvent = firstEvent;
337
+ if (
338
+ lastEvent.compEventURI &&
339
+ inputSource !== "Adf" &&
340
+ lastEvent.destinationURI
341
+ ) {
342
+ lastEvent = await waitForScanEvent(
343
+ lastEvent.destinationURI,
344
+ lastEvent.agingStamp
345
+ );
346
+ if (!lastEvent.compEventURI) {
347
+ return;
348
+ }
349
+ let startNewScanJob = await waitScanNewPageRequest(lastEvent.compEventURI);
350
+ while (startNewScanJob) {
351
+ await executeScanJob(
352
+ scanJobSettings,
353
+ inputSource,
354
+ folder,
355
+ scanCount,
356
+ scanJobContent
357
+ );
358
+ if (!lastEvent.destinationURI) {
359
+ break;
360
+ }
361
+ lastEvent = await waitForScanEvent(
362
+ lastEvent.destinationURI,
363
+ lastEvent.agingStamp
364
+ );
365
+ if (!lastEvent.compEventURI) {
366
+ return;
367
+ }
368
+ startNewScanJob = await waitScanNewPageRequest(lastEvent.compEventURI);
369
+ }
370
+ }
371
+ }
372
+
373
+ async function mergeToPdf(folder: string, scanCount: number, scanJobContent: ScanContent) {
374
+ const pdfFilePath = PathHelper.getFileForScan(
375
+ folder,
376
+ scanCount,
377
+ program.opts().pattern,
378
+ "pdf"
379
+ );
380
+ await createPdfFrom(scanJobContent, pdfFilePath);
381
+ scanJobContent.elements.forEach((e) => fs.unlink(e.path));
382
+ return pdfFilePath;
383
+ }
384
+
385
+ function displayPdfScan(pdfFilePath: string, scanJobContent: ScanContent) {
386
+ console.log(
387
+ `The following page(s) have been rendered inside '${pdfFilePath}': `
388
+ );
389
+ scanJobContent.elements.forEach((e) =>
390
+ console.log(
391
+ `\t- page ${e.pageNumber.toString().padStart(3, " ")} - ${e.width}x${
392
+ e.height
393
+ }`
394
+ )
395
+ );
396
+ }
397
+
398
+ function displayJpegScan(scanJobContent: ScanContent) {
399
+ scanJobContent.elements.forEach((e) =>
400
+ console.log(
401
+ `\t- page ${e.pageNumber.toString().padStart(3, " ")} - ${e.width}x${
402
+ e.height
403
+ } - ${e.path}`
404
+ )
405
+ );
406
+ }
407
+
408
+ async function saveScan(
409
+ event: Event,
410
+ folder: string,
411
+ scanCount: number
412
+ ): Promise<void> {
413
+ if (event.compEventURI) {
414
+ const proceedToScan = await waitScanRequest(event.compEventURI);
415
+ if (!proceedToScan) {
416
+ return;
417
+ }
418
+ }
419
+
420
+ const destination = await TryGetDestination(event);
421
+ if (!destination) {
422
+ console.log("No shortcut selected!");
423
+ return;
424
+ }
425
+ console.log("Selected shortcut: " + destination.shortcut);
426
+
427
+ const contentType = destination.getContentType();
428
+ const toPdf =
429
+ destination.shortcut === "SavePDF" || destination.shortcut === "EmailPDF";
430
+
431
+ const scanStatus = await HPApi.getScanStatus();
432
+ console.log("Afd is : " + scanStatus.adfState);
433
+
434
+ let inputSource = scanStatus.getInputSource();
435
+
436
+ let scanJobSettings = new ScanJobSettings(inputSource, contentType);
437
+
438
+ let scanJobContent: ScanContent = { elements: [] };
439
+
440
+ await executeScanJobs(
441
+ scanJobSettings,
442
+ inputSource,
443
+ folder,
444
+ scanCount,
445
+ scanJobContent,
446
+ event
447
+ );
448
+
449
+ console.log(
450
+ `Scan of page(s) completed totalPages: ${scanJobContent.elements.length}:`
451
+ );
452
+
453
+ if (toPdf) {
454
+ const pdfFilePath = await mergeToPdf(folder, scanCount, scanJobContent);
455
+ displayPdfScan(pdfFilePath, scanJobContent);
456
+ } else {
457
+ displayJpegScan(scanJobContent);
458
+ }
459
+ }
460
+
461
+ let iteration = 0;
462
+ async function init() {
463
+ const folder = await PathHelper.getOutputFolder(program.opts().directory);
464
+ console.log(`Target folder: ${folder}`);
465
+
466
+ let scanCount = 0;
467
+
468
+ let keepActive = true;
469
+ let errorCount = 0;
470
+ while (keepActive) {
471
+ console.log(`Running iteration: ${iteration} - errorCount: ${errorCount}`);
472
+ try {
473
+ let resourceURI = await register();
474
+
475
+ console.log("Waiting scan event for:", resourceURI);
476
+ const event = await waitForScanEvent(resourceURI);
477
+
478
+ scanCount++;
479
+ console.log(`Scan event captured, saving scan #${scanCount}`);
480
+ await saveScan(event, folder, scanCount);
481
+ } catch (e) {
482
+ errorCount++;
483
+ console.error(e);
484
+ console.log(e);
485
+ }
486
+
487
+ if (errorCount === 50) {
488
+ keepActive = false;
489
+ }
490
+
491
+ await delay(1000);
492
+ }
493
+ }
494
+
495
+ function findOfficejetIp(): Promise<string> {
496
+ return new Promise((resolve) => {
497
+ const bonjour = Bonjour();
498
+ console.log("Searching printer...");
499
+ let browser = bonjour.find(
500
+ {
501
+ type: "http",
502
+ },
503
+ (service) => {
504
+ console.log(".");
505
+ if (
506
+ service.name.startsWith(program.opts().name) &&
507
+ service.port === 80 &&
508
+ service.type === "http" &&
509
+ service.addresses != null
510
+ ) {
511
+ browser.stop();
512
+ bonjour.destroy();
513
+ console.log(`Found: ${service.name}`);
514
+ resolve(service.addresses[0]);
515
+ }
516
+ }
517
+ );
518
+ browser.start();
519
+ });
520
+ }
521
+
522
+ async function main() {
523
+ program.option(
524
+ "-ip, --address <ip>",
525
+ "IP address of the printer (this overrides -p)"
526
+ );
527
+ program.option(
528
+ "-n, --name <name>",
529
+ "Name of the printer for service discovery",
530
+ "HP Smart Tank Plus 570 series"
531
+ ); //or i.e. 'Deskjet 3520 series'
532
+ program.option(
533
+ "-d, --directory <dir>",
534
+ "Directory where scans are saved (defaults to /tmp/scan-to-pc<random>)"
535
+ );
536
+ program.option(
537
+ "-p, --pattern <pattern>",
538
+ 'Pattern for filename (i.e. "scan"_dd.mm.yyyy_hh:MM:ss, without this its scanPage<number>)'
539
+ );
540
+ program.option("-D, --debug", "Enable debug");
541
+ program.parse(process.argv);
542
+
543
+ let ip = program.opts().address || "192.168.1.53";
544
+ if (!ip) {
545
+ ip = await findOfficejetIp();
546
+ }
547
+
548
+ const debug = program.opts().debug != null;
549
+
550
+ HPApi.setDebug(debug);
551
+ HPApi.setPrinterIP(ip);
552
+ await init();
553
+ }
554
+
555
+ main();