Skip to content

Commit

Permalink
feat: add support for allowDuplicates; introduce DisengageStatusType …
Browse files Browse the repository at this point in the history
…enum
  • Loading branch information
0x7061 committed Dec 11, 2024
1 parent 50c25b4 commit 59237e3
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 29 deletions.
110 changes: 98 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,40 +54,125 @@ npx cap sync
import { AbrevvaBLEClient, ScanResult } from "@evva/abrevva-capacitor";

class ExampleClass {
private results: ScanResult[];
private devices: BleDevice[];

async startScan(event: any) {
this.results = [];

await AbrevvaBLEClient.requestLEScan({ timeout: 5_000 }, (result: ScanResult) => {
this.results.push(result);
this.devices = [];

await AbrevvaBLEClient.initialize()
await AbrevvaBLEClient.startScan({ timeout: 5_000 }, (device: BleDevice) => {
this.devices.push(device);
}, (success: boolean) => {
console.log(`Scan started, success: ${success}`);
}, (success: boolean) => {
console.log(`Scan stopped, success: ${success}`);
});
}
}
```

### Read EVVA component advertisement

Get the EVVA advertisement data from a scanned EVVA component.

```typescript
const ad = device.advertisementData
console.log(ad?.rssi)
console.log(ad?.isConnectable)

const md = ad?.manufacturerData
console.log(md?.batteryStatus)
console.log(md?.isOnline)
console.log(md?.officeModeEnabled)
console.log(md?.officeModeActive)
// ...
```

There are several properties that can be accessed from the advertisement.

```typescript
export interface BleDeviceAdvertisementData {
rssi?: number;
isConnectable?: boolean;
manufacturerData?: BleDeviceManufacturerData;
}

export interface BleDeviceManufacturerData {
companyIdentifier?: string;
version?: number;
componentType?: "handle" | "escutcheon" | "cylinder" | "wallreader" | "emzy" | "iobox" | "unknown";
mainFirmwareVersionMajor?: number;
mainFirmwareVersionMinor?: number;
mainFirmwareVersionPatch?: number;
componentHAL?: string;
batteryStatus?: "battery-full" | "battery-empty";
mainConstructionMode?: boolean;
subConstructionMode?: boolean;
isOnline?: boolean;
officeModeEnabled?: boolean;
twoFactorRequired?: boolean;
officeModeActive?: boolean;
identifier?: string;
subFirmwareVersionMajor?: number;
subFirmwareVersionMinor?: number;
subFirmwareVersionPatch?: number;
subComponentIdentifier?: string;
}
```

### Localize EVVA component

With the signalize method you can localize EVVA components. On a successful signalization the component will emit a melody indicating its location.
With the signalize method you can localize scanned EVVA components. On a successful signalization the component will emit a melody indicating its location.

```typescript
const success = await AbrevvaBLEClient.signalize('deviceId');
```

### Perform disengage on EVVA components
### Disengage EVVA components

For the component disengage you have to provide access credentials to the EVVA component. Those are generally acquired in the form of access media metadata from the Xesar software.

```typescript
const status = await AbrevvaBLEClient.disengage(
'deviceId',
'mobileId',
'mobileDeviceKey',
'mobileGroupId',
'mobileAccessData',
'mediumAccessData',
false,
);
```

There are several access status types upon attempting the component disengage.

```typescript
export enum DisengageStatusType {
/// Component
Authorized = "AUTHORIZED",
AuthorizedPermanentEngage = "AUTHORIZED_PERMANENT_ENGAGE",
AuthorizedPermanentDisengage = "AUTHORIZED_PERMANENT_DISENGAGE",
AuthorizedBatteryLow = "AUTHORIZED_BATTERY_LOW",
AuthorizedOffline = "AUTHORIZED_OFFLINE",
Unauthorized = "UNAUTHORIZED",
UnauthorizedOffline = "UNAUTHORIZED_OFFLINE",
SignalLocalization = "SIGNAL_LOCALIZATION",
MediumDefectOnline = "MEDIUM_DEFECT_ONLINE",
MediumBlacklisted = "MEDIUM_BLACKLISTED",
Error = "ERROR",

/// Interface
UnableToConnect = "UNABLE_TO_CONNECT",
UnableToSetNotifications = "UNABLE_TO_SET_NOTIFICATIONS",
UnableToReadChallenge = "UNABLE_TO_READ_CHALLENGE",
UnableToWriteMDF = "UNABLE_TO_WRITE_MDF",
AccessCipherError = "ACCESS_CIPHER_ERROR",
BleAdapterDisabled = "BLE_ADAPTER_DISABLED",
UnknownDevice = "UNKNOWN_DEVICE",
UnknownStatusCode = "UNKNOWN_STATUS_CODE",
Timeout = "TIMEOUT",
}
```

## API

<docgen-index>
Expand Down Expand Up @@ -147,10 +232,11 @@ const status = await AbrevvaBLEClient.disengage(

#### BleScannerOptions

| Prop | Type |
| --------------- | ------------------- |
| **`macFilter`** | <code>string</code> |
| **`timeout`** | <code>number</code> |
| Prop | Type |
| --------------------- | -------------------- |
| **`macFilter`** | <code>string</code> |
| **`allowDuplicates`** | <code>boolean</code> |
| **`timeout`** | <code>number</code> |


#### PluginListenerHandle
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ class AbrevvaPluginBLE : Plugin() {
@PluginMethod
fun startScan(call: PluginCall) {
val macFilter = call.getString("macFilter", null)
val allowDuplicates = call.getBoolean("allowDuplicates", false)
val timeout = call.getFloat("timeout", 15000.0F)!!.toLong()

this.manager.startScan({ device ->
Expand All @@ -219,7 +220,7 @@ class AbrevvaPluginBLE : Plugin() {
data.put("value", success)
notifyListeners("onScanStop", data)
call.resolve()
}, macFilter, false, timeout)
}, macFilter, allowDuplicates, timeout)
}

@PluginMethod
Expand Down
5 changes: 3 additions & 2 deletions ios/Plugin/ble/AbrevvaPluginBLE.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,14 @@ public class AbrevvaPluginBLE: CAPPlugin {
func startScan(_ call: CAPPluginCall) {
guard let bleManager = self.getBleManager(call) else { return }
let macFilter = call.getString("macFilter")
let allowDuplicates = call.getBool("allowDuplicates") ?? false
let timeout = call.getDouble("timeout").map { Int($0) } ?? nil

bleManager.startScan(
{ device in
self.bleDeviceMap[device.getAddress()] = device
let data = self.getAdvertismentData(device)
self.notifyListeners("onScanResult", data: data)
self.notifyListeners("onScanResult", data: data as [String: Any])
},
{ error in
self.notifyListeners("onScanStart", data: ["value": error == nil])
Expand All @@ -99,7 +100,7 @@ public class AbrevvaPluginBLE: CAPPlugin {
call.resolve()
},
macFilter,
false,
allowDuplicates,
timeout
)
}
Expand Down
6 changes: 3 additions & 3 deletions src/plugins/ble/client.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Capacitor } from "@capacitor/core";
import type { AbrevvaBLEClientInterface } from "./client";
import { AbrevvaBLEClient } from "./client";
import { hexStringToDataView, numbersToDataView } from "./conversion";
import type { BleDevice } from "./definitions";
import { BleDevice, DisengageStatusType } from "./definitions";
import { AbrevvaBLE } from "./plugin";

interface AbrevvaBLEClientWithPrivate extends AbrevvaBLEClientInterface {
Expand Down Expand Up @@ -274,7 +274,7 @@ describe("AbrevvaBLEClient", () => {
expect(Capacitor.getPlatform()).toBe("android");

(AbrevvaBLE.disengage as jest.Mock).mockReturnValue({
value: "ACCESS_STATUS_AUTHORIZED",
value: DisengageStatusType.Authorized,
});
const result = await AbrevvaBLEClient.disengage(mockDevice.deviceId, "", "", "", "", false);
expect(AbrevvaBLE.disengage).toHaveBeenCalledWith({
Expand All @@ -285,7 +285,7 @@ describe("AbrevvaBLEClient", () => {
mediumAccessData: "",
isPermanentRelease: false,
});
expect(result).toEqual("ACCESS_STATUS_AUTHORIZED");
expect(result).toEqual(DisengageStatusType.Authorized);
});

it("should run startNotifications", async () => {
Expand Down
38 changes: 27 additions & 11 deletions src/plugins/ble/client.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import type { PluginListenerHandle } from "@capacitor/core";

import { dataViewToHexString, hexStringToDataView } from "./conversion";
import type { InitializeOptions, TimeoutOptions, ReadResult, BleScannerOptions, BleDevice, Data } from "./definitions";
import {
BleDevice,
BleScannerOptions,
Data,
DisengageStatusType,
InitializeOptions,
ReadResult,
TimeoutOptions,
} from "./definitions";
import { AbrevvaBLE } from "./plugin";
import { getQueue } from "./queue";
import { validateUUID } from "./validators";
Expand Down Expand Up @@ -227,7 +235,7 @@ class AbrevvaBLEClientClass implements AbrevvaBLEClientInterface {
isPermanentRelease: boolean,
onConnect?: (address: string) => void,
onDisconnect?: (address: string) => void,
): Promise<string> {
): Promise<DisengageStatusType> {
return await this.queue(async () => {
if (onConnect) {
await this.eventListeners.get(`connected|${deviceId}`)?.remove();
Expand All @@ -248,16 +256,24 @@ class AbrevvaBLEClientClass implements AbrevvaBLEClientInterface {
);
}

const result = await AbrevvaBLE.disengage({
deviceId,
mobileId,
mobileDeviceKey,
mobileGroupId,
mediumAccessData,
isPermanentRelease,
});
const status = (
await AbrevvaBLE.disengage({
deviceId,
mobileId,
mobileDeviceKey,
mobileGroupId,
mediumAccessData,
isPermanentRelease,
})
).value;

return result.value;
let result: DisengageStatusType;
if (Object.values(DisengageStatusType).some((val: string) => val === status)) {
result = <DisengageStatusType>status;
} else {
result = DisengageStatusType.Error;
}
return result;
});
}

Expand Down
27 changes: 27 additions & 0 deletions src/plugins/ble/definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface TimeoutOptions {

export interface BleScannerOptions {
macFilter?: string;
allowDuplicates?: boolean;
timeout?: number;
}

Expand Down Expand Up @@ -91,6 +92,32 @@ export interface DisengageOptions {
isPermanentRelease: boolean;
}

export enum DisengageStatusType {
/// Component
Authorized = "AUTHORIZED",
AuthorizedPermanentEngage = "AUTHORIZED_PERMANENT_ENGAGE",
AuthorizedPermanentDisengage = "AUTHORIZED_PERMANENT_DISENGAGE",
AuthorizedBatteryLow = "AUTHORIZED_BATTERY_LOW",
AuthorizedOffline = "AUTHORIZED_OFFLINE",
Unauthorized = "UNAUTHORIZED",
UnauthorizedOffline = "UNAUTHORIZED_OFFLINE",
SignalLocalization = "SIGNAL_LOCALIZATION",
MediumDefectOnline = "MEDIUM_DEFECT_ONLINE",
MediumBlacklisted = "MEDIUM_BLACKLISTED",
Error = "ERROR",

/// Interface
UnableToConnect = "UNABLE_TO_CONNECT",
UnableToSetNotifications = "UNABLE_TO_SET_NOTIFICATIONS",
UnableToReadChallenge = "UNABLE_TO_READ_CHALLENGE",
UnableToWriteMDF = "UNABLE_TO_WRITE_MDF",
AccessCipherError = "ACCESS_CIPHER_ERROR",
BleAdapterDisabled = "BLE_ADAPTER_DISABLED",
UnknownDevice = "UNKNOWN_DEVICE",
UnknownStatusCode = "UNKNOWN_STATUS_CODE",
Timeout = "TIMEOUT",
}

export interface AbrevvaBLEInterface {
initialize(options?: InitializeOptions): Promise<void>;
isEnabled(): Promise<BooleanResult>;
Expand Down

0 comments on commit 59237e3

Please sign in to comment.