npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-imin-hardware

v0.2.2

Published

React Native plugin for iMin POS hardware devices

Downloads

625

Readme

react-native-imin-hardware

React Native plugin for iMin POS device hardware features.

中文文档

Supported Devices

Crane 1, Swan 1, Swan 2, Swan 2 Pro, Swift 1, Swift 2, Swift 2 Ultra, Lark 1, Falcon 1 Pro, Falcon 2, M2-Pro and other iMin Android POS devices.

Features

| Module | Description | |--------|-------------| | Device | Device info (model, serial number, brand, etc.) | | Scanner | Hardware barcode/QR code scanner | | CameraScan | Camera-based barcode/QR code scanning (ZXing + ML Kit) | | CashBox | Cash drawer control | | NFC | NFC card reading | | MSR | Magnetic stripe card reader | | RFID | UHF RFID tag read/write | | Scale | Electronic scale (serial) | | ScaleNew | Electronic scale (Android 13+ iMinEscale SDK) | | Serial | Serial port communication | | Display | Secondary display control | | Light | USB LED indicator light | | Segment | Segment display (digital tube) | | FloatingWindow | Floating window overlay |

Quick Start

1. Install

npm install react-native-imin-hardware
# or
yarn add react-native-imin-hardware

2. Android Requirements

  • minSdkVersion: 24
  • compileSdkVersion: 34+
  • React Native: 0.68+

No additional native setup required. The plugin auto-links.

3. Try it

import { Device, CameraScan } from 'react-native-imin-hardware';

// Get device model
const model = await Device.getModel();
console.log('Device:', model);

// Quick scan a barcode
const code = await CameraScan.scanQuick();
console.log('Scanned:', code);

API Reference

Device

Get device hardware information.

import { Device } from 'react-native-imin-hardware';

| Method | Return | Description | |--------|--------|-------------| | getModel() | Promise<string> | Device model name | | getSerialNumber() | Promise<string> | Device serial number | | getBrand() | Promise<string> | Device brand | | getDeviceName() | Promise<string> | Device name | | getAndroidVersion() | Promise<string> | Android SDK version number | | getAndroidVersionName() | Promise<string> | Android version name (e.g. "13") | | getSdkVersion() | Promise<string> | iMin SDK version | | getServiceVersion() | Promise<string> | iMin service version | | getDeviceInfo() | Promise<DeviceInfo> | All info in one call |

// Get all device info at once
const info = await Device.getDeviceInfo();
// info = { model, serialNumber, androidVersion, sdkVersion, brand, deviceName, ... }

Scanner (Hardware Scan Head)

Listen for barcode data from the built-in hardware scanner.

import { Scanner } from 'react-native-imin-hardware';

| Method | Return | Description | |--------|--------|-------------| | startListening() | Promise<boolean> | Start receiving scan events | | stopListening() | Promise<boolean> | Stop receiving scan events | | isConnected() | Promise<boolean> | Check scanner connection | | configure(config) | Promise<void> | Set custom broadcast action/data keys | | addListener(callback) | EmitterSubscription | Listen for scan events |

// Start listening
await Scanner.startListening();

// Listen for scan results
const subscription = Scanner.addListener((event) => {
  if (event.type === 'scanResult') {
    console.log('Code:', event.data.data);
    console.log('Type:', event.data.labelType);
  }
});

// Stop when done
await Scanner.stopListening();
subscription.remove();

Custom config (optional, for non-standard scanner broadcast):

await Scanner.configure({
  action: 'com.example.SCAN_ACTION',
  dataKey: 'barcode_string',
  byteDataKey: 'barcode_bytes',
});

CameraScan

Open the camera to scan barcodes and QR codes. Supports ZXing (default) and ML Kit engines.

import { CameraScan, BarcodeFormat, DecodeEngine } from 'react-native-imin-hardware';

Basic Scan (ZXing)

| Method | Return | Description | |--------|--------|-------------| | scan(options?) | Promise<ScanResultData> | Scan one code, returns { code, format } | | scanQuick() | Promise<string> | Scan and return code string only | | scanQRCode() | Promise<string> | Scan QR codes only | | scanBarcode() | Promise<string> | Scan 1D barcodes only | | scanAll() | Promise<ScanResultData> | Scan with all formats enabled |

// Simplest usage
const code = await CameraScan.scanQuick();

// With options
const result = await CameraScan.scan({
  formats: [BarcodeFormat.QR_CODE, BarcodeFormat.EAN_13],
  useFlash: false,
  beepEnabled: true,
  timeout: 30000, // 30s timeout, 0 = no timeout
});
console.log(result.code, result.format);

CameraScanOptions:

| Field | Type | Default | Description | |-------|------|---------|-------------| | formats | BarcodeFormatType[] | Default 4 formats | Barcode formats to recognize | | useFlash | boolean | false | Turn on flashlight | | beepEnabled | boolean | true | Play beep sound on scan | | timeout | number | 0 | Timeout in ms, 0 = no timeout |

Multi Scan (ML Kit)

| Method | Return | Description | |--------|--------|-------------| | scanMulti(options?) | Promise<ScanResultData[]> | Scan multiple codes, returns array | | isMLKitAvailable() | Promise<boolean> | Check if ML Kit is available |

// Check ML Kit availability
const mlkit = await CameraScan.isMLKitAvailable();

// Multi-barcode scan (returns array)
const results = await CameraScan.scanMulti();
results.forEach(r => console.log(r.code, r.format));

// With full options
const results = await CameraScan.scanMulti({
  formats: [BarcodeFormat.QR_CODE, BarcodeFormat.CODE_128],
  supportMultiBarcode: true,
  supportMultiAngle: true,
  fullAreaScan: true,
  areaRectRatio: 0.9,
  decodeEngine: DecodeEngine.MLKIT,
  timeout: 30000,
});

MultiScanOptions:

| Field | Type | Default | Description | |-------|------|---------|-------------| | formats | BarcodeFormatType[] | All formats | Barcode formats to recognize | | useFlash | boolean | false | Turn on flashlight | | beepEnabled | boolean | true | Play beep sound | | timeout | number | 0 | Timeout in ms | | supportMultiBarcode | boolean | true | Recognize multiple codes at once | | supportMultiAngle | boolean | true | Recognize codes at any angle | | decodeEngine | 0 \| 1 | 1 (MLKit) | 0=ZXing, 1=ML Kit | | fullAreaScan | boolean | true | Scan full camera area | | areaRectRatio | number | 0.8 | Scan area ratio (0.5~1.0) |

Supported barcode formats:

QR_CODE, EAN_13, EAN_8, UPC_A, UPC_E, CODE_128, CODE_39, CODE_93, CODABAR, ITF, RSS_14, RSS_EXPANDED, DATA_MATRIX, PDF_417, AZTEC, MAXICODE

CashBox

Control the cash drawer.

import { CashBox, CashBoxVoltage } from 'react-native-imin-hardware';

| Method | Return | Description | |--------|--------|-------------| | open() | Promise<void> | Open the cash drawer | | getStatus() | Promise<boolean> | true = open, false = closed | | setVoltage(voltage) | Promise<boolean> | Set voltage: CashBoxVoltage.V9 / V12 / V24 |

await CashBox.open();
const isOpen = await CashBox.getStatus();
await CashBox.setVoltage(CashBoxVoltage.V12);

NFC

Read NFC tags.

import { Nfc } from 'react-native-imin-hardware';

| Method | Return | Description | |--------|--------|-------------| | isAvailable() | Promise<boolean> | Device supports NFC | | isEnabled() | Promise<boolean> | NFC is turned on | | openSettings() | Promise<boolean> | Open NFC system settings | | startListening() | Promise<boolean> | Start listening for tags | | stopListening() | Promise<boolean> | Stop listening | | addListener(callback) | EmitterSubscription | Listen for tag events |

const available = await Nfc.isAvailable();
if (!available) return;

await Nfc.startListening();

const subscription = Nfc.addListener((tag) => {
  console.log('Tag ID:', tag.id);
  console.log('Content:', tag.content);
  console.log('Tech:', tag.technology);
});

// Cleanup
await Nfc.stopListening();
subscription.remove();

RFID

Read and write UHF RFID tags.

import { Rfid, RfidBank } from 'react-native-imin-hardware';

| Method | Return | Description | |--------|--------|-------------| | connect() | Promise<boolean> | Connect RFID device | | disconnect() | Promise<boolean> | Disconnect | | isConnected() | Promise<boolean> | Check connection | | startReading() | Promise<boolean> | Start continuous tag reading | | stopReading() | Promise<boolean> | Stop reading | | readTag(params?) | Promise<boolean> | Read specific memory bank | | writeTag(params) | Promise<boolean> | Write to memory bank | | writeEpc(params) | Promise<boolean> | Write new EPC | | lockTag(params) | Promise<boolean> | Lock tag memory | | killTag(password) | Promise<boolean> | Permanently kill tag | | setPower(read, write) | Promise<boolean> | Set RF power (dBm) | | setFilter(epc) | Promise<boolean> | Filter by EPC | | clearFilter() | Promise<boolean> | Clear filter | | getBatteryLevel() | Promise<number> | RFID handle battery % | | isCharging() | Promise<boolean> | Is handle charging |

await Rfid.connect();
await Rfid.startReading();

const subscription = Rfid.addTagListener((event) => {
  if (event.type === 'tag') {
    console.log('EPC:', event.epc, 'RSSI:', event.rssi);
  }
});

await Rfid.stopReading();
await Rfid.disconnect();
subscription.remove();

Scale (Serial)

Read weight data from serial-connected electronic scale.

import { Scale } from 'react-native-imin-hardware';

| Method | Return | Description | |--------|--------|-------------| | connect(path?) | Promise<boolean> | Connect, default /dev/ttyS4 | | disconnect() | Promise<boolean> | Disconnect | | tare() | Promise<boolean> | Tare (zero with load) | | zero() | Promise<boolean> | Zero (reset to 0) | | addListener(callback) | EmitterSubscription | Listen for weight data |

await Scale.connect('/dev/ttyS4');

const subscription = Scale.addListener((data) => {
  console.log('Weight:', data.weight, 'Status:', data.status);
});

await Scale.tare();
await Scale.disconnect();
subscription.remove();

ScaleNew (Android 13+)

Electronic scale using iMinEscale SDK (AIDL service).

import { ScaleNew, ScaleUnit } from 'react-native-imin-hardware';

| Method | Return | Description | |--------|--------|-------------| | connectService() | Promise<boolean> | Connect to scale service | | getData() | Promise<boolean> | Start receiving weight data | | cancelGetData() | Promise<boolean> | Stop receiving data | | zero() | Promise<boolean> | Zero | | tare() | Promise<boolean> | Tare | | digitalTare(weight) | Promise<boolean> | Digital tare (grams) | | setUnitPrice(price) | Promise<boolean> | Set unit price | | getUnitPrice() | Promise<string> | Get unit price | | setUnit(unit) | Promise<boolean> | Set weight unit | | getUnit() | Promise<number> | Get weight unit | | getServiceVersion() | Promise<string> | Service version | | getFirmwareVersion() | Promise<string> | Firmware version | | restart() | Promise<boolean> | Restart scale | | addListener(callback) | EmitterSubscription | Listen for events |

await ScaleNew.connectService();
await ScaleNew.getData();

const subscription = ScaleNew.addListener((event) => {
  if (event.type === 'weight') {
    console.log('Net:', event.net, 'Tare:', event.tare, 'Stable:', event.isStable);
  }
  if (event.type === 'price') {
    console.log('Total:', event.totalPrice);
  }
});

await ScaleNew.tare();
await ScaleNew.cancelGetData();
subscription.remove();

Serial Port

Raw serial port communication.

import { Serial } from 'react-native-imin-hardware';

| Method | Return | Description | |--------|--------|-------------| | open(path, baudRate?) | Promise<boolean> | Open serial port | | close() | Promise<boolean> | Close serial port | | write(data) | Promise<boolean> | Write bytes (comma-separated) | | writeString(text) | Promise<boolean> | Write string | | isOpen() | Promise<boolean> | Check if port is open | | addListener(callback) | EmitterSubscription | Listen for received data |

await Serial.open('/dev/ttyS4', 115200);

const subscription = Serial.addListener((event) => {
  console.log('Received:', event.data); // number[]
});

await Serial.writeString('Hello');
await Serial.write('72,101,108,108,111'); // byte values
await Serial.close();
subscription.remove();

Display (Secondary Screen)

Control the customer-facing secondary display.

import { Display } from 'react-native-imin-hardware';

| Method | Return | Description | |--------|--------|-------------| | isAvailable() | Promise<boolean> | Check if secondary display exists | | enable() | Promise<boolean> | Enable display | | disable() | Promise<boolean> | Disable display | | showText(text) | Promise<boolean> | Show text | | showImage(path) | Promise<boolean> | Show image (URL or local path) | | playVideo(path) | Promise<boolean> | Play video (loops) | | clear() | Promise<boolean> | Clear display content |

const available = await Display.isAvailable();
if (!available) return;

await Display.enable();
await Display.showText('Total: $12.50');
await Display.showImage('https://example.com/promo.png');
await Display.playVideo('https://example.com/ad.mp4');
await Display.clear();
await Display.disable();

Light (LED)

Control USB LED indicator light.

import { Light } from 'react-native-imin-hardware';

| Method | Return | Description | |--------|--------|-------------| | connect() | Promise<boolean> | Connect LED device (requests USB permission) | | turnOnGreen() | Promise<boolean> | Green light on | | turnOnRed() | Promise<boolean> | Red light on | | turnOff() | Promise<boolean> | Light off | | disconnect() | Promise<boolean> | Disconnect |

await Light.connect();
await Light.turnOnGreen();
// ... later
await Light.turnOff();
await Light.disconnect();

Segment Display

Control the segment display (digital tube).

import { Segment } from 'react-native-imin-hardware';

| Method | Return | Description | |--------|--------|-------------| | findDevice() | Promise<SegmentDeviceInfo> | Find USB segment device | | requestPermission() | Promise<boolean> | Request USB permission | | connect() | Promise<boolean> | Connect device | | sendData(data, align?) | Promise<boolean> | Display data (max 9 chars), align: 'left' or 'right' | | clear() | Promise<boolean> | Clear display | | full() | Promise<boolean> | All segments on (test) | | disconnect() | Promise<boolean> | Disconnect |

await Segment.findDevice();
await Segment.requestPermission();
await Segment.connect();
await Segment.sendData('12345', 'right');
await Segment.clear();
await Segment.disconnect();

Floating Window

Show a floating overlay window on screen.

import { FloatingWindow } from 'react-native-imin-hardware';

| Method | Return | Description | |--------|--------|-------------| | hasPermission() | Promise<boolean> | Check overlay permission | | requestPermission() | Promise<boolean> | Request permission (opens settings) | | show() | Promise<boolean> | Show floating window | | hide() | Promise<boolean> | Hide floating window | | isShowing() | Promise<boolean> | Check if visible | | updateText(text) | Promise<boolean> | Update displayed text | | setPosition(x, y) | Promise<boolean> | Set window position (px) |

if (!(await FloatingWindow.hasPermission())) {
  await FloatingWindow.requestPermission();
}
await FloatingWindow.show();
await FloatingWindow.updateText('Order #1234 - $25.00');
await FloatingWindow.setPosition(100, 200);
await FloatingWindow.hide();

MSR (Magnetic Stripe Reader)

MSR works as a keyboard input device. When a card is swiped, data is automatically typed into the currently focused TextInput. No special API calls needed beyond checking availability.

import { Msr } from 'react-native-imin-hardware';

const available = await Msr.isAvailable();

Error Handling

All async methods may throw errors. Common error codes:

| Code | Description | |------|-------------| | CANCELED | User canceled the operation | | NO_ACTIVITY | No active Android activity | | ALREADY_ACTIVE | A scan is already in progress | | NO_DATA | No result returned | | ERROR | General error (check message) |

try {
  const result = await CameraScan.scan();
} catch (e) {
  if (e.code === 'CANCELED') {
    // User pressed back
  } else {
    console.error('Scan error:', e.message);
  }
}

TypeScript Types

All types are exported and available for import:

import type {
  // Device
  DeviceInfo,
  // Scanner
  ScannerConfig, ScanResult, ScannerEvent,
  // CameraScan
  ScanResultData, CameraScanOptions, MultiScanOptions,
  BarcodeFormatType, DecodeEngineType,
  // CashBox
  CashBoxVoltage,
  // NFC
  NfcTagData, NfcEvent,
  // RFID
  RfidTagData, RfidEvent, ReadTagParams, WriteTagParams, LockTagParams,
  // Scale
  ScaleDataEvent,
  // ScaleNew
  ScaleNewEvent, ScaleNewWeightEvent, ScaleNewPriceEvent,
  // Serial
  SerialDataEvent,
  // Segment
  SegmentDeviceInfo, SegmentAlign,
} from 'react-native-imin-hardware';

scan() vs scanMulti()

| | scan() | scanMulti() | |---|---|---| | Engine | ZXing | ML Kit (with ZXing fallback) | | Returns | Single ScanResultData | Array ScanResultData[] | | Multi-barcode | No | Yes | | Multi-angle | No | Yes (any rotation) | | Use case | Simple one-code scan | Multiple codes, rotated codes | | Dependency | Built-in | Requires Google Play Services |

Use scan() for simple scenarios. Use scanMulti() when you need multi-angle or multi-barcode support.

Device Compatibility

| Module | Requirement | Notes | |--------|-------------|-------| | Device | All devices | Always available | | Scanner | Hardware scan head | Not all models have built-in scanner | | CameraScan | Camera | Requires camera permission | | CashBox | Cash drawer port | Check device specs | | NFC | NFC hardware | Use Nfc.isAvailable() to check | | MSR | MSR hardware | Keyboard input mode | | RFID | RFID handle | External UHF RFID accessory | | Scale | Serial port | Requires external scale hardware | | ScaleNew | Android 13+ | iMinEscale SDK, newer devices only | | Serial | Serial port | Device-specific port path | | Display | Secondary screen | Dual-screen devices only | | Light | USB LED | External USB LED accessory | | Segment | USB segment display | External USB accessory | | FloatingWindow | Android 6.0+ | Requires SYSTEM_ALERT_WINDOW permission |

Event Listeners

Modules that emit events (Scanner, NFC, RFID, Scale, ScaleNew, Serial) return an EmitterSubscription. Always clean up in useEffect:

useEffect(() => {
  const subscription = Scanner.addListener((event) => {
    // handle event
  });
  return () => subscription.remove();
}, []);

FAQ

Q: scan() opens camera but nothing happens? A: Make sure camera permission is granted. Check AndroidManifest.xml has <uses-permission android:name="android.permission.CAMERA" /> (the plugin adds this automatically).

Q: Which should I use, scan() or scanMulti()? A: Use scan() for simple one-code scanning. Use scanMulti() if you need to scan codes at odd angles or multiple codes at once. scan() is lighter and doesn't require Google Play Services.

Q: Scanner events not received? A: Make sure you call Scanner.startListening() first. Some devices use custom broadcast actions — use Scanner.configure() to set the correct action and data keys for your device.

Q: ScaleNew methods fail? A: ScaleNew requires Android 13+ and the iMinEscale service. Use Scale (serial) module for older devices.

Q: ML Kit not available? A: ML Kit requires Google Play Services. Call CameraScan.isMLKitAvailable() to check. If unavailable, scanMulti() falls back to ZXing automatically.

Changelog

See CHANGELOG.md for version history.

Repository

https://github.com/iminsoftware/ReactNativeApiTest

License

MIT