# @pos-360/touch-id

A Node.js native module to interface with SecuGen fingerprint readers on Windows. This module provides an intuitive API for interacting with the SecuGen Hamster SDK (sgfplib.dll).

## Features

- **Device Management:** Open, close, and retrieve device information (image dimensions, contrast, brightness, etc.) from connected SecuGen fingerprint readers.
- **Capture & Template Creation:** Capture fingerprint images and create fingerprint templates for matching.
- **Template Matching:** Compare two fingerprint templates to determine a match.
- **Auto-On Mode:**  Enable convenient "auto-on" mode to capture fingerprints as soon as a finger is placed on the reader.
- **Asynchronous Operations:** Fingerprint capture and template creation are asynchronous, allowing for non-blocking behavior.



## Installation

```bash
npm install @pos-360/touch-id
```



## Usage

### Import the module

```typescript
import { SecugenHandler, AutoOnEvent, SecugenErrors } from '@pos-360/touch-id';
```

### Create a new instance of the SecugenHandler class

```typescript
const secugen = new SecugenHandler();
```

### Open the device

```typescript
const openResult = secugen.open();

if (openResult.ok === false) {
  console.error(openResult.message);
  process.exit(1);
}

console.log("Opened device.");
```

### Get device information

```typescript
const deviceInfoResult = secugen.getDeviceInfo();

if (deviceInfoResult.ok === false) {
  console.error(deviceInfoResult.message);
} else {
  console.log("Device info:", deviceInfoResult);
}
```

### Capture a fingerprint and create a template

```typescript
const captureResult = await secugen.captureAndCreateTemplate();

if (captureResult.ok === false) {
  console.error(captureResult.message);
} else {
  console.log("Captured template:", captureResult);
}
```

### Compare two templates

```typescript
const compareResult = await secugen.compareTemplates(template1, template2);

if (compareResult.ok === false) {
  console.error(compareResult.message);
} else {
  console.log("Templates match:", compareResult.isMatch);
}
```

### Start auto-on mode

```typescript
const startAutoOnResult = await secugen.startAutoOn();

if (startAutoOnResult.ok === false) {
  console.error(startAutoOnResult.message);
} else {
  console.log("Started auto-on mode.");

  secugen.subscribeToAutoOnEvent(AutoOnEvent.Capture, (payload) => {
    if (payload.ok === false) {
      console.error(payload.message);
    } else {
      console.log("Auto-on capture:", payload);
    }
  });
}
```

### Stop auto-on mode

```typescript
const stopAutoOnResult = secugen.stopAutoOn();

if (stopAutoOnResult.ok === false) {
  console.error(stopAutoOnResult.message);
} else {
  console.log("Stopped auto-on mode.");
}
```

### Capture a fingerprint from auto-on mode

```typescript
const captureResult = await secugen.captureFromAutoOn(5_000);

if (captureResult.ok === false) {
  if (captureResult.code === SecugenErrors.CAPTURE_TIMEOUT) {
    console.log("Capture timeout.");
  } else {
    console.error(captureResult.message);
  }
} else {
  console.log("Captured from auto-on:", captureResult);
}
```

### Cancel capture from auto-on mode

```typescript
const cancelResult = await secugen.cancelCaptureFromAutoOn();

if (cancelResult.ok === false) {
  console.error(cancelResult.message);
} else {
  console.log("Cancelled capture.");
}
```

### Close the device

```typescript
const closeResult = await secugen.close();

if (closeResult.ok === false) {
  console.error(closeResult.message);
} else {
  console.log("Closed device.");
}
```

## Error handling

The `SecugenHandler` methods return objects with the following structure:

```typescript
type GoodResult = {
  ok: true;
  // ... other properties
};

type BadResult = {
  ok: false;
  code: SecugenErrorCodes;
  message: SecugenErrorMessages;
};
```

You can use the `ok` property to check if the operation was successful. If it
was not, you can use the `code` and `message` properties to get more
information about the error. The `SecugenErrors` object contains a list of all
possible error codes and messages.

## Example

```typescript
import {
  SecugenHandler,
  AutoOnEvent,
  SecugenErrors,
} from "@pos-360/touch-id";

const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

(async () => {
  const secugen = new SecugenHandler();
  const openResult = secugen.open();

  if (openResult.ok === false) {
    console.error(openResult.message);
    process.exit(1);
  }

  console.log("Opened device.");

  try {
    await secugen.startAutoOn();
    console.log("Started auto-on mode.");

    const autoTemplateData = await secugen.captureFromAutoOn();

    if (autoTemplateData.ok === false) {
      if (autoTemplateData.code === SecugenErrors.CAPTURE_TIMEOUT) {
        console.log("Capture timeout.");
      } else {
        console.error(autoTemplateData.message);
      }
    } else {
      console.log("Auto-on template data:", autoTemplateData);
    }
  } catch (err) {
    console.error(err);
  } finally {
    await secugen.close();
    console.log("Closed device.");
  }
})();
```



## API Reference

### `SecugenHandler` Class

- `new SecugenHandler()`:  Creates a new `SecugenHandler` instance.

#### Methods

- `open(): DefaultResponse`: Opens the connected SecuGen device. Returns a `DefaultResponse` object indicating success or failure.
- `close(): Promise<DefaultResponse>`: Closes the SecuGen device. Returns a Promise that resolves with a `DefaultResponse` object indicating success or failure.
- `getDeviceInfo(): DeviceInfoResult`: Returns a `DeviceInfoResult` object with device information including `DeviceID`, `ImageWidth`, `ImageHeight`, `Contrast`, `Brightness`, `Gain`, and `ImageDPI`, or an error object if the device is not opened.
- `captureAndCreateTemplate(): Promise<CaptureResult>`: Asynchronously captures a fingerprint image and creates a template. Returns a Promise that resolves with a `CaptureResult` object containing the raw image data and the base64-encoded template, or an error object if the device is not opened or auto-on mode is active.
- `startAutoOn(): Promise<DefaultResponse>`: Starts the "auto-on" mode. The provided callback function will be invoked with events when fingers are placed/removed from the reader and when captures are made. Returns a Promise that resolves with a `DefaultResponse` object indicating success or failure.
- `stopAutoOn(): DefaultResponse`: Stops the "auto-on" mode. Returns a `DefaultResponse` object indicating success or failure.
- `captureFromAutoOn(timeout?: number): Promise<CaptureResult>`: Asynchronously captures a fingerprint in "auto-on" mode. Returns a Promise that resolves with a `CaptureResult` object containing the raw image data and the base64-encoded template, or an error object if auto-on mode is not active or a timeout occurs. An optional `timeout` parameter in milliseconds can be provided.
- `cancelCaptureFromAutoOn(): Promise<DefaultResponse>`: Cancels the capture operation initiated by `captureFromAutoOn`.  Returns a Promise that resolves with a `DefaultResponse` object indicating success or failure.
- `compareTemplates(template1: string, template2: string): CompareResult`: Compares two base64-encoded fingerprint templates. Returns a `CompareResult` object indicating whether a match was found, or an error object if the device is not opened.
- `isAutoOnActive(): boolean`:  Returns a boolean indicating whether "auto-on" mode is active.
- `subscribeToAutoOnEvent<T extends AutoOnEvent>(event: T, listener: (payload: AutoOnEventPayload<T>) => void): DefaultResponse`: Subscribes to an event in auto-on mode. Returns a `DefaultResponse` object indicating success or failure. Available events are `fingerOn`, `fingerOff`, `capture`, and `error`.
- `unsubscribeFromAutoOnEvent<T extends AutoOnEvent>(event: T, listener: (payload: AutoOnEventPayload<T>) => void): DefaultResponse`: Unsubscribes from an event in auto-on mode. Returns a `DefaultResponse` object indicating success or failure.



## Troubleshooting

### Module not initialized

If you get the error `Module not initialized` or the method calls return `{ ok: false, code: 'FAILURE_TO_INIT', message: 'Module not initialized' }`, then make sure that you have called `open()` before any other methods and that the SecuGen device is connected to your computer.

### Device not found

If you get the error `Device not found`, make sure that the SecuGen device is connected to your computer and that the driver is installed.

### USB Bandwidth is not sufficient

If you get the error `USB Bandwidth is not sufficient for image transfer`, try using a different USB port or a USB hub with its own power supply.

## Building

### Troubleshooting on Windows

If you have a problem building on Windows due to a `openssl != ''` error, make the following edit to `<node-gyp path>/gyp/pylib/gyp/input.py`:

```
diff --git a/gyp/pylib/gyp/input.py b/gyp/pylib/gyp/input.py
index 354958b..bb982eb 100644
--- a/gyp/pylib/gyp/input.py
+++ b/gyp/pylib/gyp/input.py
@@ -1190,7 +1190,7 @@ def EvalSingleCondition(cond_expr, true_dict, false_dict, phase, variables, buil
         else:
             ast_code = compile(cond_expr_expanded, "<string>", "eval")
             cached_conditions_asts[cond_expr_expanded] = ast_code
-        env = {"__builtins__": {}, "v": StrictVersion}
+        env = {"__builtins__": {"openssl_fips": ""}, "v": StrictVersion}
         if eval(ast_code, env, variables):
             return true_dict
         return false_dict
```



## License

MIT