react-native-bluetooth-obd-manager
Version:
A React Native hook library to manage Bluetooth Low Energy connections and communication with ELM327 OBD-II adapters.
248 lines • 8.11 kB
TypeScript
import type { Dispatch } from 'react';
import type { Peripheral as BlePeripheral } from 'react-native-ble-manager';
export interface Peripheral extends BlePeripheral {
id: string;
name?: string;
rssi?: number;
advertising?: {
isConnectable?: boolean;
serviceUUIDs?: string[];
manufacturerData?: Buffer;
serviceData?: Record<string, Buffer>;
txPowerLevel?: number;
};
}
export type BleManagerState = 'on' | 'off' | 'turning_on' | 'turning_off' | 'unknown' | 'resetting' | 'unsupported' | 'unauthorized';
export interface BleManagerDidUpdateValueForCharacteristicEvent {
peripheral: string;
characteristic: string;
service: string;
value: number[];
}
export interface BleError {
errorCode: string;
message: string;
attErrorCode?: number;
}
/**
* Configuration details for the active BLE connection to an ELM327 device.
* Stored once a compatible service/characteristic set is found.
*/
export interface ActiveDeviceConfig {
/** The UUID of the primary service used for communication */
serviceUUID: string;
/** The UUID of the characteristic used for writing commands */
writeCharacteristicUUID: string;
/** The UUID of the characteristic used for receiving notifications */
notifyCharacteristicUUID: string;
/** Whether the write characteristic supports Write (with response) or WriteWithoutResponse */
writeType: 'Write' | 'WriteWithoutResponse';
}
/**
* Extends the base Peripheral type to include prediction flags for OBD compatibility.
*/
export interface PeripheralWithPrediction extends Peripheral {
/** Indicates if this device is likely an OBD-II adapter based on name heuristics */
isLikelyOBD?: boolean;
/** Additional information about prediction reasoning (for debugging) */
prediction?: string;
}
export interface DeferredPromise<T> {
promise: Promise<T>;
resolve: (value: T | PromiseLike<T>) => void;
reject: (reason: Error) => void;
}
export interface BleDisconnectPeripheralEvent {
peripheral: string;
reason?: string;
}
/**
* Interface representing chunked Bluetooth response data.
* Each element in the array represents a distinct data packet received.
*/
export interface ChunkedResponse {
/** The complete flattened byte array (excluding the prompt byte) */
data: Uint8Array;
/** The original response chunks as received from the device */
chunks: Uint8Array[];
}
export interface CommandExecutionState {
promise: DeferredPromise<string | Uint8Array | ChunkedResponse>;
timeoutId: NodeJS.Timeout | null;
responseBuffer: number[];
responseChunks: number[][];
expectedReturnType: 'string' | 'bytes' | 'chunked';
}
export interface CurrentCommand {
timeoutId?: NodeJS.Timeout;
responseBuffer: number[];
expectedReturnType: 'string' | 'bytes';
}
/**
* Represents the state managed by the Bluetooth context and reducer.
*/
export interface BluetoothState {
/** Whether the Bluetooth adapter is on */
isBluetoothOn: boolean;
/** Whether required permissions are granted */
hasPermissions: boolean;
/** Whether the BluetoothProvider is still initializing */
isInitializing: boolean;
/** The most recent error that occurred, if any */
error: BleError | Error | null;
isScanning: boolean;
discoveredDevices: PeripheralWithPrediction[];
isConnecting: boolean;
isDisconnecting: boolean;
connectedDevice: Peripheral | null;
activeDeviceConfig: ActiveDeviceConfig | null;
isAwaitingResponse: boolean;
isStreaming: boolean;
lastSuccessfulCommandTimestamp: number | null;
}
/**
* Actions that can be dispatched to the Bluetooth reducer.
* Uses a discriminated union based on the 'type' property.
*/
export type BluetoothAction = {
type: 'SET_INITIALIZING';
payload: boolean;
} | {
type: 'SET_BLUETOOTH_STATE';
payload: boolean;
} | {
type: 'SET_PERMISSIONS_STATUS';
payload: boolean;
} | {
type: 'SET_ERROR';
payload: BleError | Error | null;
} | {
type: 'RESET_STATE';
} | {
type: 'SCAN_START';
} | {
type: 'SCAN_STOP';
} | {
type: 'DEVICE_FOUND';
payload: PeripheralWithPrediction;
} | {
type: 'CLEAR_DISCOVERED_DEVICES';
} | {
type: 'CONNECT_START';
} | {
type: 'CONNECT_SUCCESS';
payload: {
device: Peripheral;
config: ActiveDeviceConfig;
};
} | {
type: 'CONNECT_FAILURE';
payload: BleError | Error;
} | {
type: 'DEVICE_DISCONNECTED';
} | {
type: 'DISCONNECT_START';
} | {
type: 'DISCONNECT_SUCCESS';
} | {
type: 'DISCONNECT_FAILURE';
payload: BleError | Error;
} | {
type: 'SEND_COMMAND_START';
} | {
type: 'COMMAND_SUCCESS';
payload?: string | Uint8Array;
} | {
type: 'COMMAND_FAILURE';
payload: BleError | Error;
} | {
type: 'COMMAND_TIMEOUT';
} | {
type: 'DATA_RECEIVED';
payload: number[];
} | {
type: 'SET_STREAMING_STATUS';
payload: boolean;
} | {
type: 'UPDATE_LAST_SUCCESS_TIMESTAMP';
} | {
type: 'STREAMING_INACTIVITY_TIMEOUT';
};
/**
* Type for the dispatch function provided by the Bluetooth context.
*/
export type BluetoothDispatch = Dispatch<BluetoothAction>;
/**
* Structure of the state context provided to consumers.
*/
export type BluetoothContextState = BluetoothState;
/**
* Structure of the object returned by the `useBluetooth` hook.
*/
export interface UseBluetoothResult extends BluetoothContextState {
/**
* Checks if required Bluetooth permissions are granted.
* @returns Promise resolving to true if permissions are granted, false otherwise
*/
checkPermissions: () => Promise<boolean>;
/**
* Requests Bluetooth permissions from the user.
* @returns Promise resolving to true if permissions are granted, false otherwise
*/
requestBluetoothPermissions: () => Promise<boolean>;
/**
* Prompts the user to enable Bluetooth if it's not already on.
* On iOS, this is a no-op as there's no API to open Bluetooth settings.
*/
promptEnableBluetooth: () => Promise<void>;
/**
* Scans for nearby Bluetooth devices.
* @param scanDuration Duration to scan in milliseconds (default: 5000)
*/
scanDevices: (scanDuration?: number) => Promise<void>;
/**
* Connects to a Bluetooth device.
* @param deviceId ID of the device to connect to
* @returns Promise resolving to the connected peripheral
*/
connectToDevice: (deviceId: string) => Promise<Peripheral>;
/**
* Disconnects from the currently connected device.
*/
disconnect: () => Promise<void>;
/**
* Sends a command to the connected ELM327 device and returns the response as a string.
* @param command The command to send (e.g., "AT Z", "01 0C")
* @param options Options for command execution
* @returns Promise resolving to the string response
*/
sendCommand: (command: string, options?: {
timeout?: number;
}) => Promise<string>;
/**
* Sends a command to the connected ELM327 device and returns the raw response bytes.
* @param command The command to send
* @param options Options for command execution
* @returns Promise resolving to the raw byte response
*/
sendCommandRaw: (command: string, options?: {
timeout?: number;
}) => Promise<Uint8Array>;
/**
* Sends a command to the connected ELM327 device and returns the response as
* an array of chunks, preserving the original data packet boundaries.
* @param command The command to send
* @param options Options for command execution
* @returns Promise resolving to the chunked response
*/
sendCommandRawChunked: (command: string, options?: {
timeout?: number;
}) => Promise<ChunkedResponse>;
/**
* Sets the streaming state.
* When true, enables automatic inactivity monitoring.
* @param shouldStream Whether streaming should be active
*/
setStreaming: (shouldStream: boolean) => void;
}
//# sourceMappingURL=index.d.ts.map