From f54144853d0adfd1d72bbfb65e38a9a4b8597d3c Mon Sep 17 00:00:00 2001 From: Katie Rischpater <98350084+the-bay-kay@users.noreply.github.com> Date: Thu, 7 Mar 2024 18:01:07 -0800 Subject: [PATCH 01/13] Added toggle between Scanning Modes - Setup for adding BLE Scanning - TODO: Change permissions for iOS dev menu selection --- www/i18n/en.json | 14 ++++++++-- www/js/bluetooth/BluetoothScanPage.tsx | 37 ++++++++++++++++++++++---- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/www/i18n/en.json b/www/i18n/en.json index ac4c87469..c0a69a3ad 100644 --- a/www/i18n/en.json +++ b/www/i18n/en.json @@ -232,12 +232,22 @@ "list-datepicker-set": "Set", "bluetooth": { - "scan-debug-title": "Bluetooth Scanner", - "scan-for-bluetooth": "Scan for Devices", + "title": { + "ble": "BLE Beacon Scanner", + "classic": "Bluetooth Classic Scanner" + }, + "scan": { + "for-ble": "Scan for BLE Beacons", + "for-bluetooth": "Scan for Classic Devices" + }, "is-scanning": "Scanning...", "device-info": { "id": "ID", "name": "Name" + }, + "switch-to": { + "classic": "Switch to Classic", + "ble": "Switch to BLE" } }, diff --git a/www/js/bluetooth/BluetoothScanPage.tsx b/www/js/bluetooth/BluetoothScanPage.tsx index f7c7c3c84..7ae980dca 100644 --- a/www/js/bluetooth/BluetoothScanPage.tsx +++ b/www/js/bluetooth/BluetoothScanPage.tsx @@ -18,10 +18,11 @@ const BluetoothScanPage = ({ ...props }: any) => { const { t } = useTranslation(); const [logs, setLogs] = useState([]); const [isScanning, setIsScanning] = useState(false); + const [isClassic, setIsClassic] = useState(false); const { colors } = useTheme(); // Function to run Bluetooth test and update logs - const runBluetoothTest = async () => { + const runBluetoothClassicTest = async () => { try { setIsScanning(true); const newLogs = await gatherBluetoothData(t); @@ -33,6 +34,10 @@ const BluetoothScanPage = ({ ...props }: any) => { } }; + const switchMode = () => { + setIsClassic(!isClassic); + }; + const BluetoothCardList = ({ devices }) => (
{devices.map((device) => { @@ -57,11 +62,33 @@ const BluetoothScanPage = ({ ...props }: any) => { props.onDismiss?.(); }} /> - + - + + + @@ -83,7 +110,7 @@ const BluetoothScanPage = ({ ...props }: any) => { const s = StyleSheet.create({ btnContainer: { - padding: 16, + padding: 8, justifyContent: 'center', }, btn: { From 8a1eb3bf49f4a1a0eed29289f4018d0920c2397f Mon Sep 17 00:00:00 2001 From: Katie Rischpater <98350084+the-bay-kay@users.noreply.github.com> Date: Fri, 8 Mar 2024 12:29:59 -0800 Subject: [PATCH 02/13] Added ibeacon plugin - Built successfully on iOS --- package.cordovabuild.json | 4 +++- setup/setup_shared_native.sh | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/package.cordovabuild.json b/package.cordovabuild.json index 53c28da48..ff0394e84 100644 --- a/package.cordovabuild.json +++ b/package.cordovabuild.json @@ -98,7 +98,8 @@ "ANDROID_SUPPORT_V4_VERSION": "27.+" }, "cordova-plugin-bluetooth-classic-serial-port": {}, - "cordova-custom-config": {} + "cordova-custom-config": {}, + "cordova-plugin-ibeacon": {} } }, "dependencies": { @@ -136,6 +137,7 @@ "cordova-plugin-x-socialsharing": "6.0.4", "cordova-plugin-bluetooth-classic-serial-port": "git+https://github.com/louisg1337/cordova-plugin-bluetooth-classic-serial-port.git", "cordova-custom-config": "^5.1.1", + "cordova-plugin-ibeacon": "git+https://github.com/petermetz/cordova-plugin-ibeacon.git", "core-js": "^2.5.7", "enketo-core": "^6.1.7", "enketo-transformer": "^4.0.0", diff --git a/setup/setup_shared_native.sh b/setup/setup_shared_native.sh index 87e652561..dfc52f072 100644 --- a/setup/setup_shared_native.sh +++ b/setup/setup_shared_native.sh @@ -23,7 +23,7 @@ sed -i -e "s|/usr/bin/env node|/usr/bin/env node --unhandled-rejections=strict|" npx cordova prepare -EXPECTED_COUNT=25 +EXPECTED_COUNT=26 INSTALLED_COUNT=`npx cordova plugin list | wc -l` echo "Found $INSTALLED_COUNT plugins, expected $EXPECTED_COUNT" if [ $INSTALLED_COUNT -lt $EXPECTED_COUNT ]; From cf3e5e411f4f79042efeaf1fcbfde527e033889d Mon Sep 17 00:00:00 2001 From: louisg1337 Date: Fri, 8 Mar 2024 17:31:52 -0500 Subject: [PATCH 03/13] Incorporated new iBeacon plugin we will be using --- package.cordovabuild.json | 4 +++- setup/setup_shared_native.sh | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/package.cordovabuild.json b/package.cordovabuild.json index 53c28da48..69f66e90b 100644 --- a/package.cordovabuild.json +++ b/package.cordovabuild.json @@ -98,7 +98,8 @@ "ANDROID_SUPPORT_V4_VERSION": "27.+" }, "cordova-plugin-bluetooth-classic-serial-port": {}, - "cordova-custom-config": {} + "cordova-custom-config": {}, + "cordova-plugin-ibeacon": {} } }, "dependencies": { @@ -136,6 +137,7 @@ "cordova-plugin-x-socialsharing": "6.0.4", "cordova-plugin-bluetooth-classic-serial-port": "git+https://github.com/louisg1337/cordova-plugin-bluetooth-classic-serial-port.git", "cordova-custom-config": "^5.1.1", + "cordova-plugin-ibeacon": "git+https://github.com/louisg1337/cordova-plugin-ibeacon.git", "core-js": "^2.5.7", "enketo-core": "^6.1.7", "enketo-transformer": "^4.0.0", diff --git a/setup/setup_shared_native.sh b/setup/setup_shared_native.sh index 87e652561..dfc52f072 100644 --- a/setup/setup_shared_native.sh +++ b/setup/setup_shared_native.sh @@ -23,7 +23,7 @@ sed -i -e "s|/usr/bin/env node|/usr/bin/env node --unhandled-rejections=strict|" npx cordova prepare -EXPECTED_COUNT=25 +EXPECTED_COUNT=26 INSTALLED_COUNT=`npx cordova plugin list | wc -l` echo "Found $INSTALLED_COUNT plugins, expected $EXPECTED_COUNT" if [ $INSTALLED_COUNT -lt $EXPECTED_COUNT ]; From 5cce5715b0ce2d5de8a39e5e8a56e883775b5d53 Mon Sep 17 00:00:00 2001 From: Katie Rischpater <98350084+the-bay-kay@users.noreply.github.com> Date: Fri, 8 Mar 2024 20:39:11 -0800 Subject: [PATCH 04/13] Changed scanner permissions - Added template for BLE Scanning function - Changed iOS compatibility check to only run for bluetooth classic scanner --- www/js/bluetooth/BluetoothScanPage.tsx | 17 ++++++++++++++--- www/js/control/BluetoothScanSettingRow.tsx | 3 ++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/www/js/bluetooth/BluetoothScanPage.tsx b/www/js/bluetooth/BluetoothScanPage.tsx index 7ae980dca..09137eb81 100644 --- a/www/js/bluetooth/BluetoothScanPage.tsx +++ b/www/js/bluetooth/BluetoothScanPage.tsx @@ -2,7 +2,8 @@ import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { StyleSheet, Modal, ScrollView, SafeAreaView, View } from 'react-native'; import gatherBluetoothData from './blueoothScanner'; -import { logWarn } from '../plugin/logger'; +import { logWarn, displayErrorMsg } from '../plugin/logger'; +import { getConfig } from '../config/dynamicConfig'; import BluetoothCard from './BluetoothCard'; import { Appbar, useTheme, Button } from 'react-native-paper'; @@ -21,8 +22,14 @@ const BluetoothScanPage = ({ ...props }: any) => { const [isClassic, setIsClassic] = useState(false); const { colors } = useTheme(); - // Function to run Bluetooth test and update logs const runBluetoothClassicTest = async () => { + let config = await getConfig(); + + if (!config.ios_use_remote_push) { + displayErrorMsg('Sorry, Bluetooth Classic scanning is not available on iOS!', 'OS Error:'); + return; + } + try { setIsScanning(true); const newLogs = await gatherBluetoothData(t); @@ -34,6 +41,10 @@ const BluetoothScanPage = ({ ...props }: any) => { } }; + const runBLETest = async () => { + displayErrorMsg('Not Implemented Yet!', '404:'); + }; + const switchMode = () => { setIsClassic(!isClassic); }; @@ -80,7 +91,7 @@ const BluetoothScanPage = ({ ...props }: any) => { + + + { + testLogs.map((l) => ( +
+ {l} +
+ ) + ) + } +
); From a73fd32a4534ec3a22c92145e942dc4cf96dcda2 Mon Sep 17 00:00:00 2001 From: Katie Rischpater <98350084+the-bay-kay@users.noreply.github.com> Date: Tue, 12 Mar 2024 11:53:36 -0700 Subject: [PATCH 06/13] Refactored BLE Scanner, filled out typing --- www/js/bluetooth/BluetoothScanPage.tsx | 81 +++++++++++++------------- www/js/bluetooth/blueoothScanner.ts | 75 +++++++++++++++++++++--- www/js/types/BluetoothDevices.ts | 22 +++++++ www/js/usePermissionStatus.ts | 16 ++--- 4 files changed, 137 insertions(+), 57 deletions(-) create mode 100644 www/js/types/BluetoothDevices.ts diff --git a/www/js/bluetooth/BluetoothScanPage.tsx b/www/js/bluetooth/BluetoothScanPage.tsx index 713d77ca3..b34a42d2d 100644 --- a/www/js/bluetooth/BluetoothScanPage.tsx +++ b/www/js/bluetooth/BluetoothScanPage.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { StyleSheet, Modal, ScrollView, SafeAreaView, View, Text } from 'react-native'; -import gatherBluetoothData from './blueoothScanner'; +import { gatherBluetoothData, startBLEScanning } from './blueoothScanner'; import { logWarn, displayErrorMsg } from '../plugin/logger'; import { getConfig } from '../config/dynamicConfig'; import BluetoothCard from './BluetoothCard'; @@ -43,43 +43,40 @@ const BluetoothScanPage = ({ ...props }: any) => { }; const runBLETest = async () => { - // try { - // const newLogs = await testBLE(); - // setLogs(newLogs) - // } catch (error) { - // logWarn(error) - // } - BeaconMonitor(); + //await startBLEScanning(); + BeaconMonitor(); // Will combine BeaconMonitor & StartBLE Scanning, if possible }; // BLE LOGIC const BeaconMonitor = () => { - setTestLogs([]) + setTestLogs([]); - const logToDom = message => { - setTestLogs(prevLogs => [...prevLogs, message]); + const logToDom = (message) => { + setTestLogs((prevLogs) => [...prevLogs, message]); }; - logToDom("HELLO") - logToDom("HELLO2") - + logToDom('HELLO'); + logToDom('HELLO2'); + let delegate = new window['cordova'].plugins.locationManager.Delegate(); + logToDom(delegate); delegate.didDetermineStateForRegion = function (pluginResult) { - logToDom('[BLE] didDetermineStateForRegion'); - logToDom(JSON.stringify(pluginResult)); - window['cordova'].plugins.locationManager.appendToDeviceLog('[DOM] didDetermineStateForRegion: ' - + JSON.stringify(pluginResult)); + logToDom('[BLE] didDetermineStateForRegion'); + logToDom(JSON.stringify(pluginResult)); + window['cordova'].plugins.locationManager.appendToDeviceLog( + '[DOM] didDetermineStateForRegion: ' + JSON.stringify(pluginResult), + ); }; delegate.didStartMonitoringForRegion = function (pluginResult) { - logToDom('[BLE] didStartMonitoringForRegion'); - logToDom(JSON.stringify(pluginResult)); + logToDom('[BLE] didStartMonitoringForRegion'); + logToDom(JSON.stringify(pluginResult)); }; delegate.didRangeBeaconsInRegion = function (pluginResult) { - logToDom('[BLE] didRangeBeaconsInRegion'); - logToDom(JSON.stringify(pluginResult)); + logToDom('[BLE] didRangeBeaconsInRegion'); + logToDom(JSON.stringify(pluginResult)); }; var uuid = '426C7565-4368-6172-6D42-6561636F6E73'; @@ -87,19 +84,27 @@ const BluetoothScanPage = ({ ...props }: any) => { var minor = 4949; var major = 3838; - // Use NULL for wildcard + // 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 - var beaconRegion = new window['cordova'].plugins.locationManager.BeaconRegion(identifier, uuid, major, minor); + var beaconRegion = new window['cordova'].plugins.locationManager.BeaconRegion( + identifier, + uuid, + major, + minor, + ); window['cordova'].plugins.locationManager.setDelegate(delegate); // TODO: // ADD IN iOS PERMISSION CHECKS HERE - window['cordova'].plugins.locationManager.startMonitoringForRegion(beaconRegion) - .fail(function(e) { logToDom(e); }) - .done(); + window['cordova'].plugins.locationManager + .startMonitoringForRegion(beaconRegion) + .fail(function (e) { + logToDom(e); + }) + .done(); }; const switchMode = () => { @@ -155,26 +160,18 @@ const BluetoothScanPage = ({ ...props }: any) => { {isScanning ? t('bluetooth.is-scanning') : isClassic - ? t('bluetooth.scan.for-bluetooth') - : t('bluetooth.scan.for-ble')} + ? t('bluetooth.scan.for-bluetooth') + : t('bluetooth.scan.for-ble')} - - { - testLogs.map((l) => ( -
- {l} -
- ) - ) - } + {testLogs.map((l) => ( +
{l}
+ ))}
); diff --git a/www/js/bluetooth/blueoothScanner.ts b/www/js/bluetooth/blueoothScanner.ts index c26152a83..396e868b3 100644 --- a/www/js/bluetooth/blueoothScanner.ts +++ b/www/js/bluetooth/blueoothScanner.ts @@ -1,11 +1,20 @@ import { logWarn, logDebug } from '../plugin/logger'; +import { BluetoothClassicDevice, BLEBeaconDevice } from '../types/BluetoothDevices'; -// Device data, as defined in BluetoothClassicSerial's docs -type BluetoothClassicDevice = { - class: number; - id: string; - address: string; - name: string; +const LouisTestBeacon = { + identifier: 'Louis-Beacon', + uuid: '426C7565-4368-6172-6D42-6561636F6E73', + broadcast_type: 'iBeacon', + major: '4949', + minor: '3838', +}; +const KatieTestBeacon: BLEBeaconDevice = { + // System ID: 'DD:34:02:07:EC:04' + identifier: 'BlueCharm_98105', + uuid: '426C7565-4368-6172-6D42-6561636F6E73', + broadcast_type: 'iBeacon', + major: '4949', + minor: '3838', }; /** @@ -13,7 +22,7 @@ type BluetoothClassicDevice = { * @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 gatherBluetoothData(t): Promise { return new Promise((resolve, reject) => { let logs: string[] = []; logDebug('Running bluetooth discovery test!'); @@ -71,3 +80,55 @@ export default function gatherBluetoothData(t): Promise { }); }); } + +function createBLERegion(beaconData: BLEBeaconDevice) { + // throws an error if the parameters are not valid + return new window['locationManager'].BeaconRegion( + beaconData.identifier, + beaconData.uuid, + beaconData.major, + beaconData.minor, + ); +} + +export function startBLEScanning() { + const delegate = new window['locationManager'].Delegate(); + + delegate.didDetermineStateForRegion = function (pluginResult) { + logWarn('[DOM] didDetermineStateForRegion: ' + JSON.stringify(pluginResult)); + window['locationManager'].appendToDeviceLog( + '[DOM] didDetermineStateForRegion: ' + JSON.stringify(pluginResult), + ); + }; + + delegate.didStartMonitoringForRegion = function (pluginResult) { + logWarn('didStartMonitoringForRegion:' + JSON.stringify(pluginResult)); + }; + + delegate.didRangeBeaconsInRegion = function (pluginResult) { + logWarn('[DOM] didRangeBeaconsInRegion: ' + JSON.stringify(pluginResult)); + }; + + window['locationManager'].setDelegate(delegate); + window['locationManager'].requestWhenInUseAuthorization(); + + const BeaconRegion = createBLERegion(KatieTestBeacon); + + window['locationManager'] + .startMonitoringForRegion(BeaconRegion) + .fail(function (e) { + logWarn(e); + }) + .done(); +} + +export function endBLEScanning() { + const beaconRegion = createBLERegion(KatieTestBeacon); + + window['locationManager'] + .stopMonitoringForRegion(beaconRegion) + .fail(function (e) { + console.error(e); + }) + .done(); +} diff --git a/www/js/types/BluetoothDevices.ts b/www/js/types/BluetoothDevices.ts new file mode 100644 index 000000000..e7d6f5116 --- /dev/null +++ b/www/js/types/BluetoothDevices.ts @@ -0,0 +1,22 @@ +// Device data, as defined in BluetoothClassicSerial's docs +export type BluetoothClassicDevice = { + class: number; + id: string; + address: string; + name: string; +}; + +/* 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; + broadcast_type: string; + major: string; + minor: string; +}; diff --git a/www/js/usePermissionStatus.ts b/www/js/usePermissionStatus.ts index 0654f3cf8..56fbdead1 100644 --- a/www/js/usePermissionStatus.ts +++ b/www/js/usePermissionStatus.ts @@ -137,12 +137,12 @@ const usePermissionStatus = () => { androidVersion < 6 ? 'intro.appstatus.locperms.description.android-lt-6' : androidVersion < 10 - ? 'intro.appstatus.locperms.description.android-6-9' - : androidVersion < 11 - ? 'intro.appstatus.locperms.description.android-10' - : androidVersion < 12 - ? 'intro.appstatus.locperms.description.android-11' - : 'intro.appstatus.locperms.description.android-gte-12'; + ? 'intro.appstatus.locperms.description.android-6-9' + : androidVersion < 11 + ? 'intro.appstatus.locperms.description.android-10' + : androidVersion < 12 + ? 'intro.appstatus.locperms.description.android-11' + : 'intro.appstatus.locperms.description.android-gte-12'; console.log('description tags are ' + androidSettingsDescTag + ' ' + androidPermDescTag); // location settings let locSettingsCheck = { @@ -358,8 +358,8 @@ const usePermissionStatus = () => { androidVersion == 12 ? 'intro.appstatus.unusedapprestrict.description.android-disable-12' : androidVersion < 12 - ? 'intro.appstatus.unusedapprestrict.description.android-disable-lt-12' - : 'intro.appstatus.unusedapprestrict.description.android-disable-gte-13'; + ? 'intro.appstatus.unusedapprestrict.description.android-disable-lt-12' + : 'intro.appstatus.unusedapprestrict.description.android-disable-gte-13'; let unusedAppsUnrestrictedCheck = { name: t('intro.appstatus.unusedapprestrict.name'), desc: t(androidUnusedDescTag), From 6bd3977fe696046fd2ddadc19448dec650193a70 Mon Sep 17 00:00:00 2001 From: Katie Rischpater <98350084+the-bay-kay@users.noreply.github.com> Date: Sun, 17 Mar 2024 16:36:39 -0700 Subject: [PATCH 07/13] Fixed errors caused in merge, ran Prettier - Merge to keep up with changes in bluetooth_scanner branch this PR is based off of --- www/js/bluetooth/BluetoothScanPage.tsx | 6 +++--- www/js/types/BluetoothDevices.ts | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/www/js/bluetooth/BluetoothScanPage.tsx b/www/js/bluetooth/BluetoothScanPage.tsx index 8f73e82d1..311652c94 100644 --- a/www/js/bluetooth/BluetoothScanPage.tsx +++ b/www/js/bluetooth/BluetoothScanPage.tsx @@ -24,7 +24,7 @@ const BluetoothScanPage = ({ ...props }: any) => { const { colors } = useTheme(); // Function to run Bluetooth Classic test and update logs - const runBluetoothTest = async () => { + const runBluetoothClassicTest = async () => { let permissionFunction; // Depending on user platform, handle requesting the permissions differently if (window['cordova'].platformId == 'android') { @@ -179,8 +179,8 @@ const BluetoothScanPage = ({ ...props }: any) => { {isScanning ? t('bluetooth.is-scanning') : isClassic - ? t('bluetooth.scan.for-bluetooth') - : t('bluetooth.scan.for-ble')} + ? t('bluetooth.scan.for-bluetooth') + : t('bluetooth.scan.for-ble')} - {testLogs.map((l) => ( -
{l}
+ {testLogs.map((log, index) => ( + {log} ))}
diff --git a/www/js/bluetooth/bluetoothScanner.ts b/www/js/bluetooth/bluetoothScanner.ts index d3a6cebc0..819801cbd 100644 --- a/www/js/bluetooth/bluetoothScanner.ts +++ b/www/js/bluetooth/bluetoothScanner.ts @@ -22,7 +22,7 @@ const KatieTestBeacon: BLEBeaconDevice = { * @param t is the i18next translation function * @returns an array of strings containing device data, formatted ['ID: id Name: name'] */ -export function gatherBluetoothData(t): Promise { +export function gatherBluetoothClassicData(t): Promise { return new Promise((resolve, reject) => { logDebug('Running bluetooth discovery test!'); From 989ee5fa1b492a5419155c16a96f476a7754eee2 Mon Sep 17 00:00:00 2001 From: Katie Rischpater <98350084+the-bay-kay@users.noreply.github.com> Date: Thu, 21 Mar 2024 13:20:08 -0700 Subject: [PATCH 10/13] Fixed typing files / imports --- www/js/bluetooth/BluetoothScanPage.tsx | 8 +++----- www/js/bluetooth/bluetoothScanner.ts | 12 ++++++------ www/js/types/BluetoothDevices.ts | 15 +++++++++++---- www/js/types/bluetoothTypes.ts | 8 -------- 4 files changed, 20 insertions(+), 23 deletions(-) delete mode 100644 www/js/types/bluetoothTypes.ts diff --git a/www/js/bluetooth/BluetoothScanPage.tsx b/www/js/bluetooth/BluetoothScanPage.tsx index 0344f8c91..a20d91e4f 100644 --- a/www/js/bluetooth/BluetoothScanPage.tsx +++ b/www/js/bluetooth/BluetoothScanPage.tsx @@ -5,7 +5,7 @@ import { gatherBluetoothClassicData } from './bluetoothScanner'; import { logWarn, displayError, displayErrorMsg, logDebug } from '../plugin/logger'; import BluetoothCard from './BluetoothCard'; import { Appbar, useTheme, Button } from 'react-native-paper'; -import { BluetoothClassicDevice } from '../types/BluetoothDevices'; +import { BLEPluginCallback, BluetoothClassicDevice } from '../types/bluetoothDevices'; /** * The implementation of this scanner page follows the design of @@ -68,7 +68,7 @@ const BluetoothScanPage = ({ ...props }: any) => { let delegate = new window['cordova'].plugins.locationManager.Delegate(); - delegate.didDetermineStateForRegion = function (pluginResult) { + delegate.didDetermineStateForRegion = function (pluginResult: BLEPluginCallback) { logToDom('[BLE] didDetermineStateForRegion'); logToDom(JSON.stringify(pluginResult, null, 2)); window['cordova'].plugins.locationManager.appendToDeviceLog( @@ -82,6 +82,7 @@ const BluetoothScanPage = ({ ...props }: any) => { }; delegate.didRangeBeaconsInRegion = function (pluginResult) { + // Not seeing this called... logToDom('[BLE] didRangeBeaconsInRegion'); logToDom(JSON.stringify(pluginResult)); }; @@ -169,9 +170,6 @@ const BluetoothScanPage = ({ ...props }: any) => { : t('bluetooth.scan.for-ble')} - {testLogs.map((log, index) => ( diff --git a/www/js/bluetooth/bluetoothScanner.ts b/www/js/bluetooth/bluetoothScanner.ts index 819801cbd..c0b75cd7d 100644 --- a/www/js/bluetooth/bluetoothScanner.ts +++ b/www/js/bluetooth/bluetoothScanner.ts @@ -1,20 +1,20 @@ import { logWarn, logDebug, displayError } from '../plugin/logger'; -import { BluetoothClassicDevice, BLEBeaconDevice } from '../types/BluetoothDevices'; +import { BluetoothClassicDevice, BLEBeaconDevice } from '../types/bluetoothDevices'; -const LouisTestBeacon = { +const LouisTestBeacon: BLEBeaconDevice = { identifier: 'Louis-Beacon', uuid: '426C7565-4368-6172-6D42-6561636F6E73', broadcast_type: 'iBeacon', - major: '4949', - minor: '3838', + major: 4949, + minor: 3838, }; const KatieTestBeacon: BLEBeaconDevice = { // System ID: 'DD:34:02:07:EC:04' identifier: 'BlueCharm_98105', uuid: '426C7565-4368-6172-6D42-6561636F6E73', broadcast_type: 'iBeacon', - major: '4949', - minor: '3838', + major: 4949, + minor: 3838, }; /** diff --git a/www/js/types/BluetoothDevices.ts b/www/js/types/BluetoothDevices.ts index 64b004a3f..6c711cb4f 100644 --- a/www/js/types/BluetoothDevices.ts +++ b/www/js/types/BluetoothDevices.ts @@ -4,7 +4,7 @@ export type BluetoothClassicDevice = { id: string; address: string; name: string; - is_paired?: boolean; + is_paired?: boolean; // We keep track of this, because BCS doesn't }; /* Config File containg BLEBeaconData, mapped in the format @@ -17,7 +17,14 @@ export type BluetoothClassicDevice = { export type BLEBeaconDevice = { identifier: string; uuid: string; - broadcast_type: string; - major: string; - minor: string; + major: number; + minor: number; + broadcast_type?: string; + typeName?: string; // e.g., "BeaconRegion"; used for callback +}; + +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 -}; From 2e9e948d77f2a496a5064ff023e831e4dae6eccf Mon Sep 17 00:00:00 2001 From: Katie Rischpater <98350084+the-bay-kay@users.noreply.github.com> Date: Fri, 22 Mar 2024 15:18:53 -0700 Subject: [PATCH 11/13] Refactored BLE scanner, updated UI - Scanner now displays all potential beacons - When scanning, beacon icons will change to show when they are in / out of range - Expanded typing for BLE Devices --- www/js/bluetooth/BluetoothCard.tsx | 20 ++- www/js/bluetooth/BluetoothScanPage.tsx | 164 ++++++++++++++++--------- www/js/types/BluetoothDevices.ts | 6 +- 3 files changed, 124 insertions(+), 66 deletions(-) diff --git a/www/js/bluetooth/BluetoothCard.tsx b/www/js/bluetooth/BluetoothCard.tsx index a8c2a4e08..6db1d3823 100644 --- a/www/js/bluetooth/BluetoothCard.tsx +++ b/www/js/bluetooth/BluetoothCard.tsx @@ -3,14 +3,26 @@ import { Card, List } from 'react-native-paper'; import { StyleSheet } from 'react-native'; type Props = any; -const BluetoothCard = ({ device }: Props) => { +const BluetoothCard = ({ device, isClassic }: Props) => { + if (isClassic) { + return ( + + } + /> + + ); + } return ( } + subtitle={`UUID: ...${device.uuid.slice(-13)}`} // e.g., + left={() => } /> ); diff --git a/www/js/bluetooth/BluetoothScanPage.tsx b/www/js/bluetooth/BluetoothScanPage.tsx index a20d91e4f..a02436ad7 100644 --- a/www/js/bluetooth/BluetoothScanPage.tsx +++ b/www/js/bluetooth/BluetoothScanPage.tsx @@ -5,7 +5,12 @@ import { gatherBluetoothClassicData } from './bluetoothScanner'; import { logWarn, displayError, displayErrorMsg, logDebug } from '../plugin/logger'; import BluetoothCard from './BluetoothCard'; import { Appbar, useTheme, Button } from 'react-native-paper'; -import { BLEPluginCallback, BluetoothClassicDevice } from '../types/bluetoothDevices'; +import { + BLEBeaconDevice, + BLEPluginCallback, + BluetoothClassicDevice, + BLEDeviceList, +} from '../types/bluetoothDevices'; /** * The implementation of this scanner page follows the design of @@ -17,12 +22,33 @@ import { BLEPluginCallback, BluetoothClassicDevice } from '../types/bluetoothDev const BluetoothScanPage = ({ ...props }: any) => { const { t } = useTranslation(); - const [logs, setLogs] = useState([]); - const [testLogs, setTestLogs] = useState([]); - const [isScanning, setIsScanning] = useState(false); + const [bluetoothClassicList, setBluetoothClassicList] = useState([]); + const [sampleBLEDevices, setSampleBLEDevices] = useState({ + '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 [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 runBluetoothClassicTest = async () => { // Classic not currently supported on iOS @@ -43,13 +69,13 @@ const BluetoothScanPage = ({ ...props }: any) => { } try { - setIsScanning(true); + setIsScanningClassic(true); const newLogs = await gatherBluetoothClassicData(t); - setLogs(newLogs); + setBluetoothClassicList(newLogs); } catch (error) { logWarn(error); } finally { - setIsScanning(false); + setIsScanningClassic(false); } }; @@ -58,19 +84,33 @@ const BluetoothScanPage = ({ ...props }: any) => { BeaconMonitor(); // Will combine BeaconMonitor & StartBLE Scanning, if possible }; + function setRangeStatus(uuid: string, status: boolean) { + setSampleBLEDevices((prevDevices) => { + const newList = prevDevices; + newList[uuid].in_range = status; + return newList; + }); + } + // BLE LOGIC const BeaconMonitor = () => { - setTestLogs([]); - - const logToDom = (message) => { - setTestLogs((prevLogs) => [...prevLogs, message]); - }; - let delegate = new window['cordova'].plugins.locationManager.Delegate(); delegate.didDetermineStateForRegion = function (pluginResult: BLEPluginCallback) { - logToDom('[BLE] didDetermineStateForRegion'); - logToDom(JSON.stringify(pluginResult, null, 2)); + // `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); + } else { + displayErrorMsg('Error: Unknown state recorded during BLE Scanning!'); + logWarn('Error: Unknown state recorded in BLE Scanning!'); + return; + } + logDebug('[BLE] didDetermineStateForRegion'); + logDebug(JSON.stringify(pluginResult, null, 2)); window['cordova'].plugins.locationManager.appendToDeviceLog( '[DOM] didDetermineStateForRegion: ' + JSON.stringify(pluginResult, null, 2), ); @@ -83,52 +123,61 @@ const BluetoothScanPage = ({ ...props }: any) => { delegate.didRangeBeaconsInRegion = function (pluginResult) { // Not seeing this called... - logToDom('[BLE] didRangeBeaconsInRegion'); - logToDom(JSON.stringify(pluginResult)); + logDebug('[BLE] didRangeBeaconsInRegion'); + logDebug(JSON.stringify(pluginResult)); }; - var uuid = '426C7565-4368-6172-6D42-6561636F6E73'; - var identifier = 'BlueCharm_98105'; - var minor = 4949; - var major = 3838; - - // 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 - var beaconRegion = new window['cordova'].plugins.locationManager.BeaconRegion( - identifier, - uuid, - major, - minor, - ); - window['cordova'].plugins.locationManager.setDelegate(delegate); - // TODO: - // ADD IN iOS PERMISSION CHECKS HERE - - window['cordova'].plugins.locationManager - .startMonitoringForRegion(beaconRegion) - .fail(function (e) { - logToDom(e); - }) - .done(); + // 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(); + }); }; const switchMode = () => { setIsClassic(!isClassic); }; - const BluetoothCardList = ({ devices }) => ( -
- {devices.map((device) => { - if (device) { - return ; - } - return null; - })} -
- ); + const BluetoothCardList = ({ devices }) => { + if (isClassic) { + // When in calssic 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 BlueScanContent = () => (
@@ -160,22 +209,17 @@ const BluetoothScanPage = ({ ...props }: any) => { - - - {testLogs.map((log, index) => ( - {log} - ))} - +
); diff --git a/www/js/types/BluetoothDevices.ts b/www/js/types/BluetoothDevices.ts index 6c711cb4f..e628731e2 100644 --- a/www/js/types/BluetoothDevices.ts +++ b/www/js/types/BluetoothDevices.ts @@ -19,8 +19,10 @@ export type BLEBeaconDevice = { uuid: string; major: number; minor: number; - broadcast_type?: string; - typeName?: string; // e.g., "BeaconRegion"; used for callback + 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 = { From a11b6c16c2ce56d50584208001d4ce3c500845cb Mon Sep 17 00:00:00 2001 From: Katie Rischpater <98350084+the-bay-kay@users.noreply.github.com> Date: Fri, 22 Mar 2024 19:35:10 -0700 Subject: [PATCH 12/13] Added scan stop, color to indicate beacon status - The color of beacon cards will now change depending on whether or not they are within range - Refactored scan button - Added ability to stop scanning for BLE Beacons --- www/i18n/en.json | 3 +- www/js/bluetooth/BluetoothCard.tsx | 13 +++- www/js/bluetooth/BluetoothScanPage.tsx | 101 +++++++++++++++++-------- 3 files changed, 81 insertions(+), 36 deletions(-) diff --git a/www/i18n/en.json b/www/i18n/en.json index c0a69a3ad..ffbfb8ea8 100644 --- a/www/i18n/en.json +++ b/www/i18n/en.json @@ -238,7 +238,8 @@ }, "scan": { "for-ble": "Scan for BLE Beacons", - "for-bluetooth": "Scan for Classic Devices" + "for-bluetooth": "Scan for Classic Devices", + "stop": "Stop Scanning" }, "is-scanning": "Scanning...", "device-info": { diff --git a/www/js/bluetooth/BluetoothCard.tsx b/www/js/bluetooth/BluetoothCard.tsx index 6db1d3823..4af8240c4 100644 --- a/www/js/bluetooth/BluetoothCard.tsx +++ b/www/js/bluetooth/BluetoothCard.tsx @@ -1,9 +1,10 @@ import React from 'react'; -import { Card, List } from 'react-native-paper'; +import { Card, List, useTheme } from 'react-native-paper'; import { StyleSheet } from 'react-native'; type Props = any; -const BluetoothCard = ({ device, isClassic }: Props) => { +const BluetoothCard = ({ device, isClassic, isScanningBLE }: Props) => { + const { colors } = useTheme(); if (isClassic) { return ( @@ -16,8 +17,14 @@ const BluetoothCard = ({ device, isClassic }: Props) => { ); } + + let bgColor = colors.onPrimary; // 'rgba(225,225,225,1)' + if (isScanningBLE) { + bgColor = device.in_range ? `rgba(200,250,200,1)` : `rgba(250,200,200,1)`; + } + return ( - + { in_range: false, }, }); - const [isScanningClassic, setIsScanningClassic] = useState(false); + const [isScanningBLE, setIsScanningBLE] = useState(false); const [isClassic, setIsClassic] = useState(false); const { colors } = useTheme(); @@ -49,8 +49,9 @@ const BluetoothScanPage = ({ ...props }: any) => { ...device, })); } + // Function to run Bluetooth Classic test and update logs - const runBluetoothClassicTest = async () => { + async function runBluetoothClassicTest() { // Classic not currently supported on iOS if (window['cordova'].platformId == 'ios') { displayErrorMsg('Sorry, iOS is not supported!', 'OSError'); @@ -77,23 +78,22 @@ const BluetoothScanPage = ({ ...props }: any) => { } finally { setIsScanningClassic(false); } - }; - - const runBLETest = async () => { - //await startBLEScanning(); - BeaconMonitor(); // Will combine BeaconMonitor & StartBLE Scanning, if possible - }; + } function setRangeStatus(uuid: string, status: boolean) { - setSampleBLEDevices((prevDevices) => { - const newList = prevDevices; - newList[uuid].in_range = status; - return newList; - }); + setSampleBLEDevices((prevDevices) => ({ + ...prevDevices, + [uuid]: { + ...prevDevices[uuid], + in_range: status, + }, + })); } // BLE LOGIC - const BeaconMonitor = () => { + async function startBeaconScanning() { + setIsScanningBLE(true); + let delegate = new window['cordova'].plugins.locationManager.Delegate(); delegate.didDetermineStateForRegion = function (pluginResult: BLEPluginCallback) { @@ -147,7 +147,27 @@ const BluetoothScanPage = ({ ...props }: any) => { }) .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); @@ -172,13 +192,43 @@ const BluetoothScanPage = ({ ...props }: any) => {
{beaconsAsArray.map((beacon) => { if (beacon) { - return ; + return ; } })}
); }; + const ScanButton = () => { + if (isClassic) { + return ( + + + + ); + } + // else, if BLE + return ( + + + + ); + }; + const BlueScanContent = () => (
{ - - - +
); From 71abee31629d0e5103af067af264e21fc30e2a24 Mon Sep 17 00:00:00 2001 From: Katie Rischpater <98350084+the-bay-kay@users.noreply.github.com> Date: Fri, 22 Mar 2024 20:54:47 -0700 Subject: [PATCH 13/13] Cleanup Scanner Code, fix iOS bug --- www/js/bluetooth/BluetoothScanPage.tsx | 6 +-- www/js/bluetooth/bluetoothScanner.ts | 72 +------------------------- 2 files changed, 3 insertions(+), 75 deletions(-) diff --git a/www/js/bluetooth/BluetoothScanPage.tsx b/www/js/bluetooth/BluetoothScanPage.tsx index b0620cbf6..52996b321 100644 --- a/www/js/bluetooth/BluetoothScanPage.tsx +++ b/www/js/bluetooth/BluetoothScanPage.tsx @@ -104,10 +104,6 @@ const BluetoothScanPage = ({ ...props }: any) => { setRangeStatus(pluginResult.region.uuid.toUpperCase(), true); } else if (pluginResult.state == 'CLRegionStateOutside') { setRangeStatus(pluginResult.region.uuid.toUpperCase(), false); - } else { - displayErrorMsg('Error: Unknown state recorded during BLE Scanning!'); - logWarn('Error: Unknown state recorded in BLE Scanning!'); - return; } logDebug('[BLE] didDetermineStateForRegion'); logDebug(JSON.stringify(pluginResult, null, 2)); @@ -175,7 +171,7 @@ const BluetoothScanPage = ({ ...props }: any) => { const BluetoothCardList = ({ devices }) => { if (isClassic) { - // When in calssic mode, render devices as normal + // When in classic mode, render devices as normal return (
{devices.map((device) => { diff --git a/www/js/bluetooth/bluetoothScanner.ts b/www/js/bluetooth/bluetoothScanner.ts index c0b75cd7d..d7cb2d297 100644 --- a/www/js/bluetooth/bluetoothScanner.ts +++ b/www/js/bluetooth/bluetoothScanner.ts @@ -1,21 +1,5 @@ -import { logWarn, logDebug, displayError } from '../plugin/logger'; -import { BluetoothClassicDevice, BLEBeaconDevice } from '../types/bluetoothDevices'; - -const LouisTestBeacon: BLEBeaconDevice = { - identifier: 'Louis-Beacon', - uuid: '426C7565-4368-6172-6D42-6561636F6E73', - broadcast_type: 'iBeacon', - major: 4949, - minor: 3838, -}; -const KatieTestBeacon: BLEBeaconDevice = { - // System ID: 'DD:34:02:07:EC:04' - identifier: 'BlueCharm_98105', - uuid: '426C7565-4368-6172-6D42-6561636F6E73', - broadcast_type: 'iBeacon', - major: 4949, - minor: 3838, -}; +import { logDebug, displayError } from '../plugin/logger'; +import { BluetoothClassicDevice } from '../types/bluetoothDevices'; /** * gatherBluetoothData scans for viewable Bluetooth Classic Devices @@ -68,55 +52,3 @@ export function gatherBluetoothClassicData(t): Promise }); }); } - -function createBLERegion(beaconData: BLEBeaconDevice) { - // throws an error if the parameters are not valid - return new window['locationManager'].BeaconRegion( - beaconData.identifier, - beaconData.uuid, - beaconData.major, - beaconData.minor, - ); -} - -export function startBLEScanning() { - const delegate = new window['locationManager'].Delegate(); - - delegate.didDetermineStateForRegion = function (pluginResult) { - logWarn('[DOM] didDetermineStateForRegion: ' + JSON.stringify(pluginResult)); - window['locationManager'].appendToDeviceLog( - '[DOM] didDetermineStateForRegion: ' + JSON.stringify(pluginResult), - ); - }; - - delegate.didStartMonitoringForRegion = function (pluginResult) { - logWarn('didStartMonitoringForRegion:' + JSON.stringify(pluginResult)); - }; - - delegate.didRangeBeaconsInRegion = function (pluginResult) { - logWarn('[DOM] didRangeBeaconsInRegion: ' + JSON.stringify(pluginResult)); - }; - - window['locationManager'].setDelegate(delegate); - window['locationManager'].requestWhenInUseAuthorization(); - - const BeaconRegion = createBLERegion(KatieTestBeacon); - - window['locationManager'] - .startMonitoringForRegion(BeaconRegion) - .fail(function (e) { - logWarn(e); - }) - .done(); -} - -export function endBLEScanning() { - const beaconRegion = createBLERegion(KatieTestBeacon); - - window['locationManager'] - .stopMonitoringForRegion(beaconRegion) - .fail(function (e) { - console.error(e); - }) - .done(); -}