npm package: @opentechalliance/react-native-sdk
Description: A React Native SDK for OpenTech hardware
Author: R6 Digital
Latest version: 3.3.9

# react-native-sdk

An OpenTech SDK for React Native applications.

## Prerequisites

The SDK requires the following packages to be installed as part of your application:

- [react](https://www.npmjs.com/package/react)
- [react-native](https://www.npmjs.com/package/react-native)
- [react-native-ble-plx](https://www.npmjs.com/package/react-native-ble-plx)
- [react-native-kontaktio](https://www.npmjs.com/package/react-native-kontaktio)
- [react-native-quick-crypto](https://www.npmjs.com/package/react-native-quick-crypto)

Both Android and iOS applications are supported.

Supported versions:

- IOS 15.1
- Android 7.0
- JDK 17
- Node 20

## Getting started

```sh
npm install react-native-ble-plx
npm install react-native-quick-crypto
npm install @opentechalliance/react-native-sdk
```

If you are using Expo, update the `plugins` section of your `app.json` or `app.config.js` file to add the plugins for both this package and `react-native-ble-plx`:

```json
{
  "expo": {
    "plugins": [
      "react-native-ble-plx",
      "@opentechalliance/react-native-sdk"
    ]
  }
}
```

This plugin automatically configures the necessary native permissions for Bluetooth scanning.

### Android

If you are not using Expo, you will need to update the `AndroidManifest.xml` yourself to add an extra attribute to
one of the permissions.

```xml
<manifest>
  <!-- Before: -->
  <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
  <!-- After: -->
  <uses-permission android:name="android.permission.BLUETOOTH_SCAN" tools:remove="android:usesPermissionFlags" />
</manifest>
```

The SDK automatically requests the following permissions:
- `BLUETOOTH_SCAN`
- `BLUETOOTH_CONNECT`
- `ACCESS_FINE_LOCATION`

### iOS

If you are not using Expo, add the following to your `Info.plist`:

```xml
<key>NSBluetoothAlwaysUsageDescription</key>
<string>This app uses Bluetooth to connect to OpenTech devices</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app uses location services to scan for nearby devices</string>
```

### Patch Package

This SDK includes patches for `react-native-kontaktio`. These patches are automatically applied during installation via `patch-package`.

If you encounter issues, try:

```bash
npx patch-package
```

## Usage

![Flowchart](./example.png)

### Hook - useBleManager

```tsx
const { scanNearbyDevices, stopScanning, foundDevices, bleManager } = useBleManager({ additionalServiceUUIDs, onDeviceFound, enableBleScanWithoutBeacon });
```

This hook uses the `BleManager` from `react-native-ble-plx` to scan for nearby OpenTech devices.

When you call `scanNearbyDevices`, the phone will start scanning for OpenTech devices. Found devices will be added to
the `foundDevices` object, which should then be passed to the `useOpenTechDevice` hook.

Once the scanning starts, it will continue indefinitely until you call `stopScanning` or the component using the hook
is destroyed.

#### Parameters

* `additionalServiceUUIDs?: string[]` - (Optional) An array of additional service UUIDs to scan for (in addition to
  OpenTech devices).
* `enableBleScanWithoutBeacon?: boolean` - (Optional) A boolean whether BLE scans should proceed without checking for nearby beacons. Setting to `true` disables beacon scanning, and would prevent Keypad access, but SmartLocks would still show. Defaults to `false`.
* `onDeviceFound?: (device: Device) => string | null` - (Optional) A callback function that is called when a device is
  found that matches one of the `additionalServiceUUIDs`. If your callback returns null, the device will be ignored.
  If it returns a string, the device will be added to the `foundDevices` object.

#### Returns

* `scanNearbyDevices: () => Promise<void>` - Starts scanning for OpenTech devices.
* `stopScanning: () => Promise<void>` - Stops scanning for OpenTech devices.
* `foundDevices: NearbyDevices` - An object containing the devices found during the scan.
* `bleManager: BleManager` - Provides access to the internal BleManager instance.

### Hook - useOpenTechDevice

```tsx
const { isAvailable, unavailableReason, isOpening, open } = useOpenTechDevice({ foundDevices, device, onOpen });
```

To be able to trigger a device to open, you need to use the `useOpenTechDevice` hook. This hooks requires you to pass
in the `keypadId` and `code` of the device you want to open. The hook will then match these against the `foundDevices`
object returned from `useBleManager`.

If the details match a device in range, the `open` function will trigger the device to open. Otherwise, `isAvailable`
will return false and `unavailableReason` will give you a reason why.

> [!WARNING iOS]
> Due to a technical limitation, there is no guarantee that the device being connected to is the corresponding `keypadId`.
> This is a limitation of the iOS Bluetooth stack and is not specific to this SDK. The SDK will indicate that the
> keypad with the strongest adverstising signal is considerable available, but this may not be the keypad that is
> actually connected to.

#### Parameters


* `foundDevices: NearbyDevices` - The `foundDevices` object returned from `useBleManager`.
* `device: OpenTechDevice` - An object containing the device details. This can be one of:
  * `OpenTechBeacon`:
    * `provider: 'opentech_beacon'` - Identifies the device as an OpenTech beacon. Included for forward compatibility.
    * `keypadId: number` - The keypad ID of the device, as taken from the OpenTech online portal.
    * `code: string` - The access code that will be used to request the device to open. NOTE: this is typically interpreted as a number so you may need to remove leading zeros.
  * `OpenTechSmartLock`:
    * `provider: 'opentech_smartlock'` - Identifies the device as an OpenTech smart lock.
    * `facilityId: number` - The facility ID associated with the smart lock.
    * `code: string` - The access code that will be used to request the device to open.
    * `unitName: string` - The unit name associated with the smart lock.
* `onOpen?: (device: OpenTechDevice) => void` - (Optional) A callback function to be called when the device is unlocked.

#### Returns

* `isAvailable: boolean` - Indicates if the device is in range.
* `unavailableReason: string` - If `isAvailable` is false, `unavailableReason` will contain the reason why.
* `isOpening: boolean` - Indicates if the device is in the process of being opened by the app.
* `open: () => Promise<void>` - Opens the device.

### Provider - LoggerProvider

Allows you to provide a custom logger to the SDK. Logger must be compatible with [abslog](https://www.npmjs.com/package/abslog).
By default, the console logger will be used.

```tsx
const logger = getMyLogger();

<LoggerProvider logger={logger}>
  <App />
</LoggerProvider>
```

## Error handling

The SDK will throw a JavaScript error if it encounters an error. You should catch these errors in your application and
handle them appropriately. All the errors thrown by the SDK will be instances of `OpenTechError` and will contain,
at minimum, a message and error code.

* `OpenTechError` - The base error class for all errors thrown by the SDK. All other error types extend this class.
  * `message: string` - A human-readable error message.
  * `code: string` - A machine-readable error code.
* `UnlockError` - Errors that occur when requesting a device unlock.
  * `device: OpenTechDevice` - The device that was being unlocked.
* `ScanError` - Errors that occur when scanning for devices.
* `AppPermissionError` - Errors relating the app having insufficient permissions.
  * `permission: string` - The permission that caused the error.

## SDK Documentation
Documentation can be generated via running `npm run doc`. The generated docs can be found in `/doc` once the command has finished running.

## License

Apache 2.0