feat(companion): implement ble

This commit is contained in:
2026-01-12 22:24:33 +00:00
parent 8305726f83
commit 75cfbe8dd4
55 changed files with 3457 additions and 142 deletions

View File

@@ -0,0 +1,7 @@
export { Ble, defaultBleState } from "./native";
export { BleBluetoothState } from "./types";
export type {
BleNativeModuleEvents,
BleStatePayload,
BleUuids,
} from "./types";

View File

@@ -0,0 +1,90 @@
import { NativeModule, requireNativeModule } from "expo";
import { Platform } from "react-native";
import type { EventSubscription } from "expo-modules-core";
import {
BleBluetoothState,
type BleNativeModuleEvents,
type BleStatePayload,
type BleUuids,
} from "./types";
declare class ArisBleNativeModule extends NativeModule<BleNativeModuleEvents> {
start: () => void;
stop: () => void;
setAdvertisingEnabled: (enabled: boolean) => void;
setWifiRequested: (requested: boolean) => void;
sendOpaque: (payload: string, msgType?: number) => void;
getState: () => BleStatePayload;
serviceUUID: string;
feedTxUUID: string;
controlRxUUID: string;
wifiRequestTxUUID: string;
}
const isSupported = Platform.OS === "ios";
const nativeModule = isSupported
? requireNativeModule<ArisBleNativeModule>("ArisBle")
: null;
export const defaultBleState: BleStatePayload = {
bluetoothState: BleBluetoothState.Unknown,
advertisingEnabled: true,
isAdvertising: false,
isSubscribed: false,
subscribedCount: 0,
lastMsgIdSent: 0,
lastPingAt: null,
lastCommand: null,
notifyQueueDepth: 0,
droppedNotifyPackets: 0,
lastNotifyAt: null,
lastDataAt: null,
wifiRequested: false,
};
const emptyUuids: BleUuids = {
serviceUUID: "",
feedTxUUID: "",
controlRxUUID: "",
wifiRequestTxUUID: "",
};
type BleApi = {
isSupported: boolean;
start: () => void;
stop: () => void;
setAdvertisingEnabled: (enabled: boolean) => void;
setWifiRequested: (requested: boolean) => void;
sendOpaque: (payload: string, msgType?: number) => void;
getState: () => BleStatePayload;
addStateListener: (
listener: (state: BleStatePayload) => void,
) => EventSubscription | undefined;
getUuids: () => BleUuids;
};
export const Ble: BleApi = {
isSupported,
start: () => nativeModule?.start?.(),
stop: () => nativeModule?.stop?.(),
setAdvertisingEnabled: (enabled: boolean) =>
nativeModule?.setAdvertisingEnabled?.(enabled),
setWifiRequested: (requested: boolean) =>
nativeModule?.setWifiRequested?.(requested),
sendOpaque: (payload: string, msgType?: number) =>
nativeModule?.sendOpaque?.(payload, msgType),
getState: () => nativeModule?.getState?.() ?? { ...defaultBleState },
addStateListener: (listener: (state: BleStatePayload) => void) =>
nativeModule?.addListener("onStateChange", listener),
getUuids: (): BleUuids =>
nativeModule
? {
serviceUUID: nativeModule.serviceUUID,
feedTxUUID: nativeModule.feedTxUUID,
controlRxUUID: nativeModule.controlRxUUID,
wifiRequestTxUUID: nativeModule.wifiRequestTxUUID,
}
: emptyUuids,
};

View File

@@ -0,0 +1,39 @@
export const BleBluetoothState = {
Unknown: "Unknown",
Resetting: "Resetting",
Unsupported: "Unsupported",
Unauthorized: "Unauthorized",
PoweredOff: "Powered Off",
PoweredOn: "Powered On",
Other: "Other",
} as const;
export type BleBluetoothState =
(typeof BleBluetoothState)[keyof typeof BleBluetoothState];
export type BleStatePayload = {
bluetoothState: BleBluetoothState;
advertisingEnabled: boolean;
isAdvertising: boolean;
isSubscribed: boolean;
subscribedCount: number;
lastMsgIdSent: number;
lastPingAt: number | null;
lastCommand: string | null;
notifyQueueDepth: number;
droppedNotifyPackets: number;
lastNotifyAt: number | null;
lastDataAt: number | null;
wifiRequested: boolean;
};
export type BleUuids = {
serviceUUID: string;
feedTxUUID: string;
controlRxUUID: string;
wifiRequestTxUUID: string;
};
export type BleNativeModuleEvents = {
onStateChange: (state: BleStatePayload) => void;
};