({
+ '426C7565-4368-6172-6D42-6561636F6E74': {
+ identifier: 'Katie_BLEBeacon',
+ minor: 4949,
+ major: 3838,
+ in_range: false,
+ },
+ '426C7565-4368-6172-6D42-6561636F6E73': {
+ identifier: 'Louis-Beacon',
+ minor: 4949,
+ major: 3838,
+ in_range: false,
+ },
+ });
+ const [isScanningClassic, setIsScanningClassic] = useState(false);
+ const [isScanningBLE, setIsScanningBLE] = useState(false);
+ const [isClassic, setIsClassic] = useState(false);
const { colors } = useTheme();
+ // Flattens the `sampleBeacons` into an array of BLEBeaconDevices
+ function beaconsToArray() {
+ return Object.entries(sampleBLEDevices).map(([uuid, device]) => ({
+ uuid,
+ ...device,
+ }));
+ }
+
// Function to run Bluetooth Classic test and update logs
- const runBluetoothTest = async () => {
+ async function runBluetoothClassicTest() {
// Classic not currently supported on iOS
if (window['cordova'].platformId == 'ios') {
displayErrorMsg('Sorry, iOS is not supported!', 'OSError');
@@ -40,26 +70,160 @@ const BluetoothScanPage = ({ ...props }: any) => {
}
try {
- setIsScanning(true);
- const newLogs = await gatherBluetoothData(t);
- setLogs(newLogs);
+ setIsScanningClassic(true);
+ const newLogs = await gatherBluetoothClassicData(t);
+ setBluetoothClassicList(newLogs);
} catch (error) {
logWarn(error);
} finally {
- setIsScanning(false);
+ setIsScanningClassic(false);
+ }
+ }
+
+ function setRangeStatus(uuid: string, status: boolean) {
+ setSampleBLEDevices((prevDevices) => ({
+ ...prevDevices,
+ [uuid]: {
+ ...prevDevices[uuid],
+ in_range: status,
+ },
+ }));
+ }
+
+ // BLE LOGIC
+ async function startBeaconScanning() {
+ setIsScanningBLE(true);
+
+ let delegate = new window['cordova'].plugins.locationManager.Delegate();
+
+ delegate.didDetermineStateForRegion = function (pluginResult: BLEPluginCallback) {
+ // `stateInside`is returned when the user enters the beacon region
+ // `StateOutside` is either (i) left region, or (ii) started scanner (outside region)
+ if (pluginResult.state == 'CLRegionStateInside') {
+ // need toUpperCase(), b/c callback returns with only lowercase values...
+ setRangeStatus(pluginResult.region.uuid.toUpperCase(), true);
+ } else if (pluginResult.state == 'CLRegionStateOutside') {
+ setRangeStatus(pluginResult.region.uuid.toUpperCase(), false);
+ }
+ logDebug('[BLE] didDetermineStateForRegion');
+ logDebug(JSON.stringify(pluginResult, null, 2));
+ window['cordova'].plugins.locationManager.appendToDeviceLog(
+ '[DOM] didDetermineStateForRegion: ' + JSON.stringify(pluginResult, null, 2),
+ );
+ };
+
+ delegate.didStartMonitoringForRegion = function (pluginResult) {
+ logDebug('[BLE] didStartMonitoringForRegion');
+ logDebug(JSON.stringify(pluginResult));
+ };
+
+ delegate.didRangeBeaconsInRegion = function (pluginResult) {
+ // Not seeing this called...
+ logDebug('[BLE] didRangeBeaconsInRegion');
+ logDebug(JSON.stringify(pluginResult));
+ };
+
+ window['cordova'].plugins.locationManager.setDelegate(delegate);
+
+ // Setup regions for each beacon
+ beaconsToArray().forEach((sampleBeacon: BLEBeaconDevice) => {
+ // Use NULL for wildcard
+ // Need UUID value on iOS only, not Android (2nd parameter)
+ // https://stackoverflow.com/questions/38580410/how-to-scan-all-nearby-ibeacons-using-coordova-based-hybrid-application
+ const beaconRegion = new window['cordova'].plugins.locationManager.BeaconRegion(
+ sampleBeacon.identifier,
+ sampleBeacon.uuid,
+ sampleBeacon.major,
+ sampleBeacon.minor,
+ );
+ window['cordova'].plugins.locationManager
+ .startMonitoringForRegion(beaconRegion)
+ .fail(function (e) {
+ logWarn(e);
+ })
+ .done();
+ });
+ }
+
+ async function stopBeaconScanning() {
+ setIsScanningBLE(false);
+
+ beaconsToArray().forEach((sampleBeacon: BLEBeaconDevice) => {
+ setRangeStatus(sampleBeacon.uuid, false); // "zero out" the beacons
+ const beaconRegion = new window['cordova'].plugins.locationManager.BeaconRegion(
+ sampleBeacon.identifier,
+ sampleBeacon.uuid,
+ sampleBeacon.major,
+ sampleBeacon.minor,
+ );
+ window['cordova'].plugins.locationManager
+ .stopMonitoringForRegion(beaconRegion)
+ .fail(function (e) {
+ logWarn(e);
+ })
+ .done();
+ });
+ }
+
+ const switchMode = () => {
+ setIsClassic(!isClassic);
+ };
+
+ const BluetoothCardList = ({ devices }) => {
+ if (isClassic) {
+ // When in classic mode, render devices as normal
+ return (
+
+ {devices.map((device) => {
+ if (device) {
+ return ;
+ }
+ return null;
+ })}
+
+ );
}
+ const beaconsAsArray = beaconsToArray();
+ return (
+
+ {beaconsAsArray.map((beacon) => {
+ if (beacon) {
+ return ;
+ }
+ })}
+
+ );
};
- const BluetoothCardList = ({ devices }) => (
-
- {devices.map((device) => {
- if (device) {
- return ;
- }
- return null;
- })}
-
- );
+ const ScanButton = () => {
+ if (isClassic) {
+ return (
+
+
+
+ );
+ }
+ // else, if BLE
+ return (
+
+
+
+ );
+ };
const BlueScanContent = () => (
@@ -72,14 +236,23 @@ const BluetoothScanPage = ({ ...props }: any) => {
props.onDismiss?.();
}}
/>
-
+
-
-
+
+
);
@@ -98,7 +271,7 @@ const BluetoothScanPage = ({ ...props }: any) => {
const s = StyleSheet.create({
btnContainer: {
- padding: 16,
+ padding: 8,
justifyContent: 'center',
},
btn: {
diff --git a/www/js/bluetooth/bluetoothScanner.ts b/www/js/bluetooth/bluetoothScanner.ts
index 2e77c30b0..d7cb2d297 100644
--- a/www/js/bluetooth/bluetoothScanner.ts
+++ b/www/js/bluetooth/bluetoothScanner.ts
@@ -1,12 +1,12 @@
import { logDebug, displayError } from '../plugin/logger';
-import { BluetoothClassicDevice } from '../types/bluetoothTypes';
+import { BluetoothClassicDevice } from '../types/bluetoothDevices';
/**
* gatherBluetoothData scans for viewable Bluetooth Classic Devices
* @param t is the i18next translation function
* @returns an array of strings containing device data, formatted ['ID: id Name: name']
*/
-export default function gatherBluetoothData(t): Promise {
+export function gatherBluetoothClassicData(t): Promise {
return new Promise((resolve, reject) => {
logDebug('Running bluetooth discovery test!');
diff --git a/www/js/types/BluetoothDevices.ts b/www/js/types/BluetoothDevices.ts
new file mode 100644
index 000000000..e628731e2
--- /dev/null
+++ b/www/js/types/BluetoothDevices.ts
@@ -0,0 +1,32 @@
+// Device data, as defined in BluetoothClassicSerial's docs
+export type BluetoothClassicDevice = {
+ class: number;
+ id: string;
+ address: string;
+ name: string;
+ is_paired?: boolean; // We keep track of this, because BCS doesn't
+};
+
+/* Config File containg BLEBeaconData, mapped in the format
+ * UID_KEY: {Device_Info}
+ *
+ * This is set up for how a JSON file would store this data; we
+ * will most likely change this later on!
+ */
+
+export type BLEBeaconDevice = {
+ identifier: string;
+ uuid: string;
+ major: number;
+ minor: number;
+ type_name?: string; // e.g., "BeaconRegion"; used for callback
+};
+export type BLEDeviceList = {
+ [key: string]: { identifier: string; minor: number; major: number; in_range: boolean };
+};
+
+export type BLEPluginCallback = {
+ region: BLEBeaconDevice;
+ eventType: string;
+ state: string;
+};
diff --git a/www/js/types/bluetoothTypes.ts b/www/js/types/bluetoothTypes.ts
deleted file mode 100644
index c812966c2..000000000
--- a/www/js/types/bluetoothTypes.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-// Device data, as defined in BluetoothClassicSerial's docs
-export type BluetoothClassicDevice = {
- class: number;
- id: string;
- address: string;
- name: string;
- is_paired?: boolean; // We keep track of this, BCS doesn't
-};