diff options
| author | jdlugosz963 <jdlugosz963@gmail.com> | 2023-12-11 15:31:44 +0100 |
|---|---|---|
| committer | jdlugosz963 <jdlugosz963@gmail.com> | 2023-12-11 15:32:33 +0100 |
| commit | 1749bd5e7fa959a022472defb8ba78c689c994af (patch) | |
| tree | b591bf4d58c83065033cc845b56d6883782b7938 | |
| parent | f0600c5ec4ffa5a84f6a08c07ec341a22e0430de (diff) | |
| download | cyberbadge_mobile-1749bd5e7fa959a022472defb8ba78c689c994af.tar.gz cyberbadge_mobile-1749bd5e7fa959a022472defb8ba78c689c994af.zip | |
Add initial ble-service.
| -rw-r--r-- | App.tsx | 118 | ||||
| -rw-r--r-- | __tests__/App.test.tsx | 2 | ||||
| -rw-r--r-- | android/app/src/debug/AndroidManifest.xml | 42 | ||||
| -rw-r--r-- | android/app/src/main/AndroidManifest.xml | 53 | ||||
| -rwxr-xr-x | dev-env | 10 | ||||
| -rw-r--r-- | index.js | 2 | ||||
| -rw-r--r-- | package-lock.json | 21 | ||||
| -rw-r--r-- | package.json | 4 | ||||
| -rw-r--r-- | src/App.tsx | 107 | ||||
| -rw-r--r-- | src/ble-service.ts | 186 |
10 files changed, 412 insertions, 133 deletions
diff --git a/App.tsx b/App.tsx deleted file mode 100644 index 125fe1b..0000000 --- a/App.tsx +++ /dev/null | |||
| @@ -1,118 +0,0 @@ | |||
| 1 | /** | ||
| 2 | * Sample React Native App | ||
| 3 | * https://github.com/facebook/react-native | ||
| 4 | * | ||
| 5 | * @format | ||
| 6 | */ | ||
| 7 | |||
| 8 | import React from 'react'; | ||
| 9 | import type {PropsWithChildren} from 'react'; | ||
| 10 | import { | ||
| 11 | SafeAreaView, | ||
| 12 | ScrollView, | ||
| 13 | StatusBar, | ||
| 14 | StyleSheet, | ||
| 15 | Text, | ||
| 16 | useColorScheme, | ||
| 17 | View, | ||
| 18 | } from 'react-native'; | ||
| 19 | |||
| 20 | import { | ||
| 21 | Colors, | ||
| 22 | DebugInstructions, | ||
| 23 | Header, | ||
| 24 | LearnMoreLinks, | ||
| 25 | ReloadInstructions, | ||
| 26 | } from 'react-native/Libraries/NewAppScreen'; | ||
| 27 | |||
| 28 | type SectionProps = PropsWithChildren<{ | ||
| 29 | title: string; | ||
| 30 | }>; | ||
| 31 | |||
| 32 | function Section({children, title}: SectionProps): React.JSX.Element { | ||
| 33 | const isDarkMode = useColorScheme() === 'dark'; | ||
| 34 | return ( | ||
| 35 | <View style={styles.sectionContainer}> | ||
| 36 | <Text | ||
| 37 | style={[ | ||
| 38 | styles.sectionTitle, | ||
| 39 | { | ||
| 40 | color: isDarkMode ? Colors.white : Colors.black, | ||
| 41 | }, | ||
| 42 | ]}> | ||
| 43 | {title} | ||
| 44 | </Text> | ||
| 45 | <Text | ||
| 46 | style={[ | ||
| 47 | styles.sectionDescription, | ||
| 48 | { | ||
| 49 | color: isDarkMode ? Colors.light : Colors.dark, | ||
| 50 | }, | ||
| 51 | ]}> | ||
| 52 | {children} | ||
| 53 | </Text> | ||
| 54 | </View> | ||
| 55 | ); | ||
| 56 | } | ||
| 57 | |||
| 58 | function App(): React.JSX.Element { | ||
| 59 | const isDarkMode = useColorScheme() === 'dark'; | ||
| 60 | |||
| 61 | const backgroundStyle = { | ||
| 62 | backgroundColor: isDarkMode ? Colors.darker : Colors.lighter, | ||
| 63 | }; | ||
| 64 | |||
| 65 | return ( | ||
| 66 | <SafeAreaView style={backgroundStyle}> | ||
| 67 | <StatusBar | ||
| 68 | barStyle={isDarkMode ? 'light-content' : 'dark-content'} | ||
| 69 | backgroundColor={backgroundStyle.backgroundColor} | ||
| 70 | /> | ||
| 71 | <ScrollView | ||
| 72 | contentInsetAdjustmentBehavior="automatic" | ||
| 73 | style={backgroundStyle}> | ||
| 74 | <Header /> | ||
| 75 | <View | ||
| 76 | style={{ | ||
| 77 | backgroundColor: isDarkMode ? Colors.black : Colors.white, | ||
| 78 | }}> | ||
| 79 | <Section title="Step One"> | ||
| 80 | Edit <Text style={styles.highlight}>App.tsx</Text> to change this | ||
| 81 | screen and then come back to see your edits. | ||
| 82 | </Section> | ||
| 83 | <Section title="See Your Changes"> | ||
| 84 | <ReloadInstructions /> | ||
| 85 | </Section> | ||
| 86 | <Section title="Debug"> | ||
| 87 | <DebugInstructions /> | ||
| 88 | </Section> | ||
| 89 | <Section title="Learn More"> | ||
| 90 | Read the docs to discover what to do next: | ||
| 91 | </Section> | ||
| 92 | <LearnMoreLinks /> | ||
| 93 | </View> | ||
| 94 | </ScrollView> | ||
| 95 | </SafeAreaView> | ||
| 96 | ); | ||
| 97 | } | ||
| 98 | |||
| 99 | const styles = StyleSheet.create({ | ||
| 100 | sectionContainer: { | ||
| 101 | marginTop: 32, | ||
| 102 | paddingHorizontal: 24, | ||
| 103 | }, | ||
| 104 | sectionTitle: { | ||
| 105 | fontSize: 24, | ||
| 106 | fontWeight: '600', | ||
| 107 | }, | ||
| 108 | sectionDescription: { | ||
| 109 | marginTop: 8, | ||
| 110 | fontSize: 18, | ||
| 111 | fontWeight: '400', | ||
| 112 | }, | ||
| 113 | highlight: { | ||
| 114 | fontWeight: '700', | ||
| 115 | }, | ||
| 116 | }); | ||
| 117 | |||
| 118 | export default App; | ||
diff --git a/__tests__/App.test.tsx b/__tests__/App.test.tsx index 9eac6fb..d5d3244 100644 --- a/__tests__/App.test.tsx +++ b/__tests__/App.test.tsx | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | import 'react-native'; | 5 | import 'react-native'; |
| 6 | import React from 'react'; | 6 | import React from 'react'; |
| 7 | import App from '../App'; | 7 | import App from '../src/App.tsx'; |
| 8 | 8 | ||
| 9 | // Note: import explicitly to use the types shipped with jest. | 9 | // Note: import explicitly to use the types shipped with jest. |
| 10 | import {it} from '@jest/globals'; | 10 | import {it} from '@jest/globals'; |
diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml index eb98c01..b7c3a3c 100644 --- a/android/app/src/debug/AndroidManifest.xml +++ b/android/app/src/debug/AndroidManifest.xml | |||
| @@ -1,6 +1,46 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
| 2 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" | 2 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" |
| 3 | xmlns:tools="http://schemas.android.com/tools"> | 3 | xmlns:tools="http://schemas.android.com/tools"> |
| 4 | |||
| 5 | <uses-permission android:name="android.permission.INTERNET" /> | ||
| 6 | |||
| 7 | <!-- | ||
| 8 | HACK: this permission should not be needed on android 12+ devices anymore, | ||
| 9 | but in fact some manufacturers still need it for BLE to properly work : | ||
| 10 | https://stackoverflow.com/a/72370969 | ||
| 11 | --> | ||
| 12 | <uses-permission android:name="android.permission.BLUETOOTH" tools:remove="android:maxSdkVersion" /> | ||
| 13 | <!-- | ||
| 14 | should normally only be needed on android < 12 if you want to: | ||
| 15 | - activate bluetooth programmatically | ||
| 16 | - discover local BLE devices | ||
| 17 | see: https://developer.android.com/guide/topics/connectivity/bluetooth/permissions#discover-local-devices. | ||
| 18 | Same as above, may still be wrongly needed by some manufacturers on android 12+. | ||
| 19 | --> | ||
| 20 | <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" tools:remove="android:maxSdkVersion" /> | ||
| 21 | |||
| 22 | <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="28"/> | ||
| 23 | <uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30"/> | ||
| 24 | |||
| 25 | <!-- Only when targeting Android 12 or higher --> | ||
| 26 | <!-- | ||
| 27 | Please make sure you read the following documentation | ||
| 28 | to have a better understanding of the new permissions. | ||
| 29 | https://developer.android.com/guide/topics/connectivity/bluetooth/permissions#assert-never-for-location | ||
| 30 | --> | ||
| 31 | |||
| 32 | <!-- Needed if your app search for Bluetooth devices. --> | ||
| 33 | <!-- | ||
| 34 | If your app doesn't use Bluetooth scan results to derive physical location information, | ||
| 35 | you can strongly assert that your app doesn't derive physical location. | ||
| 36 | --> | ||
| 37 | <uses-permission android:name="android.permission.BLUETOOTH_SCAN" | ||
| 38 | android:usesPermissionFlags="neverForLocation" /> | ||
| 39 | <!-- Needed if you want to interact with a BLE device. --> | ||
| 40 | <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> | ||
| 41 | <!-- Needed if your app makes the current device discoverable to other Bluetooth devices. --> | ||
| 42 | <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" /> | ||
| 43 | |||
| 4 | 44 | ||
| 5 | <application | 45 | <application |
| 6 | android:usesCleartextTraffic="true" | 46 | android:usesCleartextTraffic="true" |
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 4122f36..e1c8b57 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml | |||
| @@ -1,25 +1,58 @@ | |||
| 1 | <manifest xmlns:android="http://schemas.android.com/apk/res/android"> | 1 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" |
| 2 | xmlns:tools="http://schemas.android.com/tools"> | ||
| 2 | 3 | ||
| 3 | <uses-permission android:name="android.permission.INTERNET" /> | 4 | <uses-permission android:name="android.permission.INTERNET" /> |
| 4 | 5 | ||
| 5 | <application | 6 | <uses-permission android:name="android.permission.BLUETOOTH" tools:remove="android:maxSdkVersion" /> |
| 7 | <!-- | ||
| 8 | should normally only be needed on android < 12 if you want to: | ||
| 9 | - activate bluetooth programmatically | ||
| 10 | - discover local BLE devices | ||
| 11 | see: https://developer.android.com/guide/topics/connectivity/bluetooth/permissions#discover-local-devices. | ||
| 12 | Same as above, may still be wrongly needed by some manufacturers on android 12+. | ||
| 13 | --> | ||
| 14 | <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" tools:remove="android:maxSdkVersion" /> | ||
| 15 | |||
| 16 | <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="28"/> | ||
| 17 | <uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30"/> | ||
| 18 | |||
| 19 | <!-- Only when targeting Android 12 or higher --> | ||
| 20 | <!-- | ||
| 21 | Please make sure you read the following documentation | ||
| 22 | to have a better understanding of the new permissions. | ||
| 23 | https://developer.android.com/guide/topics/connectivity/bluetooth/permissions#assert-never-for-location | ||
| 24 | --> | ||
| 25 | |||
| 26 | <!-- Needed if your app search for Bluetooth devices. --> | ||
| 27 | <!-- | ||
| 28 | If your app doesn't use Bluetooth scan results to derive physical location information, | ||
| 29 | you can strongly assert that your app doesn't derive physical location. | ||
| 30 | --> | ||
| 31 | <uses-permission android:name="android.permission.BLUETOOTH_SCAN" | ||
| 32 | android:usesPermissionFlags="neverForLocation" /> | ||
| 33 | <!-- Needed if you want to interact with a BLE device. --> | ||
| 34 | <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> | ||
| 35 | <!-- Needed if your app makes the current device discoverable to other Bluetooth devices. --> | ||
| 36 | <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" /> | ||
| 37 | |||
| 38 | <application | ||
| 6 | android:name=".MainApplication" | 39 | android:name=".MainApplication" |
| 7 | android:label="@string/app_name" | 40 | android:label="@string/app_name" |
| 8 | android:icon="@mipmap/ic_launcher" | 41 | android:icon="@mipmap/ic_launcher" |
| 9 | android:roundIcon="@mipmap/ic_launcher_round" | 42 | android:roundIcon="@mipmap/ic_launcher_round" |
| 10 | android:allowBackup="false" | 43 | android:allowBackup="false" |
| 11 | android:theme="@style/AppTheme"> | 44 | android:theme="@style/AppTheme"> |
| 12 | <activity | 45 | <activity |
| 13 | android:name=".MainActivity" | 46 | android:name=".MainActivity" |
| 14 | android:label="@string/app_name" | 47 | android:label="@string/app_name" |
| 15 | android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode" | 48 | android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode" |
| 16 | android:launchMode="singleTask" | 49 | android:launchMode="singleTask" |
| 17 | android:windowSoftInputMode="adjustResize" | 50 | android:windowSoftInputMode="adjustResize" |
| 18 | android:exported="true"> | 51 | android:exported="true"> |
| 19 | <intent-filter> | 52 | <intent-filter> |
| 20 | <action android:name="android.intent.action.MAIN" /> | 53 | <action android:name="android.intent.action.MAIN" /> |
| 21 | <category android:name="android.intent.category.LAUNCHER" /> | 54 | <category android:name="android.intent.category.LAUNCHER" /> |
| 22 | </intent-filter> | 55 | </intent-filter> |
| 23 | </activity> | 56 | </activity> |
| 24 | </application> | 57 | </application> |
| 25 | </manifest> | 58 | </manifest> |
| @@ -0,0 +1,10 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | |||
| 3 | docker run -it \ | ||
| 4 | --rm \ | ||
| 5 | -v "$(pwd)":/app -w /app \ | ||
| 6 | -v react-native-android-data:/root \ | ||
| 7 | --privileged \ | ||
| 8 | reactnativecommunity/react-native-android \ | ||
| 9 | $@ | ||
| 10 | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | */ | 3 | */ |
| 4 | 4 | ||
| 5 | import {AppRegistry} from 'react-native'; | 5 | import {AppRegistry} from 'react-native'; |
| 6 | import App from './App'; | 6 | import App from './src/App.tsx'; |
| 7 | import {name as appName} from './app.json'; | 7 | import {name as appName} from './app.json'; |
| 8 | 8 | ||
| 9 | AppRegistry.registerComponent(appName, () => App); | 9 | AppRegistry.registerComponent(appName, () => App); |
diff --git a/package-lock.json b/package-lock.json index 930ef30..f52af31 100644 --- a/package-lock.json +++ b/package-lock.json | |||
| @@ -9,7 +9,9 @@ | |||
| 9 | "version": "0.0.1", | 9 | "version": "0.0.1", |
| 10 | "dependencies": { | 10 | "dependencies": { |
| 11 | "react": "18.2.0", | 11 | "react": "18.2.0", |
| 12 | "react-native": "0.73.0" | 12 | "react-native": "0.73.0", |
| 13 | "react-native-ble-manager": "^11.0.5", | ||
| 14 | "react-native-toast-message": "^2.1.8" | ||
| 13 | }, | 15 | }, |
| 14 | "devDependencies": { | 16 | "devDependencies": { |
| 15 | "@babel/core": "^7.20.0", | 17 | "@babel/core": "^7.20.0", |
| @@ -11769,6 +11771,23 @@ | |||
| 11769 | "react": "18.2.0" | 11771 | "react": "18.2.0" |
| 11770 | } | 11772 | } |
| 11771 | }, | 11773 | }, |
| 11774 | "node_modules/react-native-ble-manager": { | ||
| 11775 | "version": "11.0.5", | ||
| 11776 | "resolved": "https://registry.npmjs.org/react-native-ble-manager/-/react-native-ble-manager-11.0.5.tgz", | ||
| 11777 | "integrity": "sha512-d8q6YEKyNkiygCy+qHa+N139UHRSWtbz7S+29mL0ZVjCSdzA6PRPsnSVDeRQJEVrWzP8ut2O6py57b6mPnBBFA==", | ||
| 11778 | "peerDependencies": { | ||
| 11779 | "react-native": ">=0.60.0" | ||
| 11780 | } | ||
| 11781 | }, | ||
| 11782 | "node_modules/react-native-toast-message": { | ||
| 11783 | "version": "2.1.8", | ||
| 11784 | "resolved": "https://registry.npmjs.org/react-native-toast-message/-/react-native-toast-message-2.1.8.tgz", | ||
| 11785 | "integrity": "sha512-vlDQVkvpSq9L3/GRfoKaxVQyenj/A7yHToC9M0nrSCTR9XPXzoc2AOgRQnM6GVvDhetDld9ZfGtFcucyWcGQNA==", | ||
| 11786 | "peerDependencies": { | ||
| 11787 | "react": "*", | ||
| 11788 | "react-native": "*" | ||
| 11789 | } | ||
| 11790 | }, | ||
| 11772 | "node_modules/react-native/node_modules/@jest/types": { | 11791 | "node_modules/react-native/node_modules/@jest/types": { |
| 11773 | "version": "26.6.2", | 11792 | "version": "26.6.2", |
| 11774 | "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", | 11793 | "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", |
diff --git a/package.json b/package.json index c51391e..7455929 100644 --- a/package.json +++ b/package.json | |||
| @@ -11,7 +11,9 @@ | |||
| 11 | }, | 11 | }, |
| 12 | "dependencies": { | 12 | "dependencies": { |
| 13 | "react": "18.2.0", | 13 | "react": "18.2.0", |
| 14 | "react-native": "0.73.0" | 14 | "react-native": "0.73.0", |
| 15 | "react-native-ble-manager": "^11.0.5", | ||
| 16 | "react-native-toast-message": "^2.1.8" | ||
| 15 | }, | 17 | }, |
| 16 | "devDependencies": { | 18 | "devDependencies": { |
| 17 | "@babel/core": "^7.20.0", | 19 | "@babel/core": "^7.20.0", |
diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..7972da9 --- /dev/null +++ b/src/App.tsx | |||
| @@ -0,0 +1,107 @@ | |||
| 1 | import React, {useEffect, useState} from 'react'; | ||
| 2 | import type {PropsWithChildren} from 'react'; | ||
| 3 | import { | ||
| 4 | Button, | ||
| 5 | FlatList, | ||
| 6 | SafeAreaView, | ||
| 7 | ScrollView, | ||
| 8 | StatusBar, | ||
| 9 | StyleSheet, | ||
| 10 | Text, | ||
| 11 | useColorScheme, | ||
| 12 | View, | ||
| 13 | } from 'react-native'; | ||
| 14 | |||
| 15 | import Toast from 'react-native-toast-message'; | ||
| 16 | import BleService, {Peripheral} from './ble-service'; | ||
| 17 | |||
| 18 | const bleService = new BleService(); | ||
| 19 | |||
| 20 | function App(): React.JSX.Element { | ||
| 21 | bleService: BleService; | ||
| 22 | |||
| 23 | const [isScanning, setIsScanning] = useState<boolean>(false); | ||
| 24 | const [peripherals, setPeripherals] = useState< | ||
| 25 | Map<Peripheral['id'], Peripheral> | ||
| 26 | >([]); | ||
| 27 | |||
| 28 | useEffect(() => { | ||
| 29 | bleService.setEvents({ | ||
| 30 | bleManagerConnectPeripheral: e => console.log(e.peripheral), | ||
| 31 | bleManagerStartSuccess: () => { | ||
| 32 | Toast.show({ | ||
| 33 | type: 'info', | ||
| 34 | text1: 'BleManager started..', | ||
| 35 | }); | ||
| 36 | }, | ||
| 37 | bleManagerDiscoverPeripheral: p => { | ||
| 38 | console.log(p.advertising.localName); | ||
| 39 | }, | ||
| 40 | bleManagerStopScan: () => { | ||
| 41 | setIsScanning(false); | ||
| 42 | setPeripherals(bleService.getPeripherals()); | ||
| 43 | |||
| 44 | console.log('scan: stop;'); | ||
| 45 | Toast.show({ | ||
| 46 | type: 'success', | ||
| 47 | text1: 'Scan stop..', | ||
| 48 | position: 'bottom', | ||
| 49 | }); | ||
| 50 | }, | ||
| 51 | bleManagerStartScan: () => { | ||
| 52 | setIsScanning(true); | ||
| 53 | console.log('scan: start;'); | ||
| 54 | Toast.show({ | ||
| 55 | type: 'info', | ||
| 56 | text1: 'Scan start..', | ||
| 57 | position: 'bottom', | ||
| 58 | }); | ||
| 59 | }, | ||
| 60 | bleManagerDidUpdateValueForCharacteristic: a => { | ||
| 61 | console.log(a.value); | ||
| 62 | }, | ||
| 63 | }); | ||
| 64 | |||
| 65 | return () => bleService.destroy(); | ||
| 66 | }); | ||
| 67 | |||
| 68 | return ( | ||
| 69 | <> | ||
| 70 | <Toast /> | ||
| 71 | <View> | ||
| 72 | <Text>Hello, World!!!!</Text> | ||
| 73 | {isScanning ? <Text>SKANUJE!</Text> : <Text>Juz nie</Text>} | ||
| 74 | <Button | ||
| 75 | onPress={() => { | ||
| 76 | bleService.scan(); | ||
| 77 | }} | ||
| 78 | title="Scan!" | ||
| 79 | /> | ||
| 80 | <FlatList | ||
| 81 | data={[...peripherals.values()]} | ||
| 82 | renderItem={({item}) => ( | ||
| 83 | <> | ||
| 84 | <Text>id: {item.id}</Text> | ||
| 85 | <Text>name: {item.name}</Text> | ||
| 86 | <Button | ||
| 87 | onPress={() => { | ||
| 88 | bleService.connect(item); | ||
| 89 | }} | ||
| 90 | title="Connect!" | ||
| 91 | /> | ||
| 92 | <Button | ||
| 93 | onPress={() => { | ||
| 94 | console.log(bleService.read(item)); | ||
| 95 | }} | ||
| 96 | title="Read!" | ||
| 97 | /> | ||
| 98 | <Text>-------</Text> | ||
| 99 | </> | ||
| 100 | )} | ||
| 101 | /> | ||
| 102 | </View> | ||
| 103 | </> | ||
| 104 | ); | ||
| 105 | } | ||
| 106 | |||
| 107 | export default App; | ||
diff --git a/src/ble-service.ts b/src/ble-service.ts new file mode 100644 index 0000000..f2a08a3 --- /dev/null +++ b/src/ble-service.ts | |||
| @@ -0,0 +1,186 @@ | |||
| 1 | import BleManager, { | ||
| 2 | BleConnectPeripheralEvent, | ||
| 3 | BleDisconnectPeripheralEvent, | ||
| 4 | BleManagerDidUpdateValueForCharacteristicEvent, | ||
| 5 | BleScanCallbackType, | ||
| 6 | BleScanMatchMode, | ||
| 7 | BleScanMode, | ||
| 8 | Peripheral as PeripheralWithoutConnectInfo, | ||
| 9 | } from 'react-native-ble-manager'; | ||
| 10 | import { | ||
| 11 | EmitterSubscription, | ||
| 12 | NativeEventEmitter, | ||
| 13 | NativeModules, | ||
| 14 | PermissionsAndroid, | ||
| 15 | Platform, | ||
| 16 | } from 'react-native'; | ||
| 17 | |||
| 18 | import Buffer from 'buffer'; | ||
| 19 | |||
| 20 | export type Peripheral = PeripheralWithoutConnectInfo & { | ||
| 21 | connected?: boolean; | ||
| 22 | connecting?: boolean; | ||
| 23 | }; | ||
| 24 | |||
| 25 | type Events = { | ||
| 26 | bleManagerStartSuccess: () => void | undefined; | ||
| 27 | bleManagerStartError: () => void | undefined; | ||
| 28 | bleManagerStopScan: () => void | undefined; | ||
| 29 | bleManagerStartScan: () => void | undefined; | ||
| 30 | bleManagerDiscoverPeripheral: (peripheral: Peripheral) => void | undefined; | ||
| 31 | bleManagerDisconnectPeripheral: ( | ||
| 32 | event: BleDisconnectPeripheralEvent, | ||
| 33 | ) => void | undefined; | ||
| 34 | bleManagerDidUpdateValueForCharacteristic: ( | ||
| 35 | event: BleManagerDidUpdateValueForCharacteristicEvent, | ||
| 36 | ) => void | undefined; | ||
| 37 | bleManagerConnectPeripheral: ( | ||
| 38 | event: BleConnectPeripheralEvent, | ||
| 39 | ) => void | undefined; | ||
| 40 | }; | ||
| 41 | |||
| 42 | export default class BleService { | ||
| 43 | private _bleManagerModule = NativeModules.BleManager; | ||
| 44 | private _bleManagerEmitter = new NativeEventEmitter(this._bleManagerModule); | ||
| 45 | private _events: Events; | ||
| 46 | private _listeners: EmitterSubscription[]; | ||
| 47 | private _peripherals: Map<Peripheral['id'], Peripheral>; | ||
| 48 | |||
| 49 | constructor() { | ||
| 50 | this._listeners = []; | ||
| 51 | this._events = {}; | ||
| 52 | this._peripherals = new Map(); | ||
| 53 | BleManager.start({showAlert: false}) | ||
| 54 | .then(() => { | ||
| 55 | this.runEvent(this._events.bleManagerStartSuccess); | ||
| 56 | console.debug('[BleService]: BleManager started.'); | ||
| 57 | }) | ||
| 58 | .catch((err: any) => { | ||
| 59 | this.runEvent(this._events.bleManagerStartError); | ||
| 60 | console.debug('[BleService]: BeManager could not be started.', err); | ||
| 61 | }); | ||
| 62 | this.handle_permissions(); | ||
| 63 | } | ||
| 64 | |||
| 65 | setEvents(events: Events) { | ||
| 66 | this._events = events; | ||
| 67 | this.setupListiners(events); | ||
| 68 | } | ||
| 69 | |||
| 70 | handle_permissions() { | ||
| 71 | // po requestach, jak sie zrobi .then() nastepnie gdy (resoult) cos zwroci to oznacza, że ma perma jak nie to nie ma. | ||
| 72 | if (Platform.OS === 'android') { | ||
| 73 | if (Platform.Version >= 31) | ||
| 74 | PermissionsAndroid.requestMultiple([ | ||
| 75 | PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN, | ||
| 76 | PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT, | ||
| 77 | ]); | ||
| 78 | else if (Platform.Version >= 23) | ||
| 79 | PermissionsAndroid.request( | ||
| 80 | PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION, | ||
| 81 | ); | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | scan() { | ||
| 86 | this.runEvent(this._events.bleManagerStartScan); | ||
| 87 | this.clearPeripherals(); | ||
| 88 | BleManager.scan([], 3, false, { | ||
| 89 | matchMode: BleScanMatchMode.Sticky, | ||
| 90 | scanMode: BleScanMode.LowLatency, | ||
| 91 | callbackType: BleScanCallbackType.AllMatches, | ||
| 92 | }) | ||
| 93 | .then(() => { | ||
| 94 | console.debug('[startScan] scan promise returned successfully.'); | ||
| 95 | }) | ||
| 96 | .catch((err: any) => { | ||
| 97 | console.error('[startScan] ble scan returned in error', err); | ||
| 98 | }); | ||
| 99 | } | ||
| 100 | |||
| 101 | destroy() { | ||
| 102 | this.destroyListiners(); | ||
| 103 | } | ||
| 104 | |||
| 105 | getPeripherals() { | ||
| 106 | return this._peripherals; | ||
| 107 | } | ||
| 108 | |||
| 109 | connect(p: Peripheral) { | ||
| 110 | return BleManager.connect(p.id); | ||
| 111 | } | ||
| 112 | |||
| 113 | async read(peripheral: Peripheral) { | ||
| 114 | await BleManager.requestMTU(peripheral.id, 512); | ||
| 115 | BleManager.startNotification( | ||
| 116 | peripheral.id, | ||
| 117 | '6e400001-b5a3-f393-e0a9-e50e24dcca9e', | ||
| 118 | '6e400003-b5a3-f393-e0a9-e50e24dcca9e', | ||
| 119 | ) | ||
| 120 | .then(a => { | ||
| 121 | // Success code | ||
| 122 | console.log('--------'); | ||
| 123 | console.log(a); | ||
| 124 | console.log('--------'); | ||
| 125 | }) | ||
| 126 | .catch(error => { | ||
| 127 | // Failure code | ||
| 128 | console.log(error); | ||
| 129 | }); | ||
| 130 | } | ||
| 131 | |||
| 132 | private addPeripheral(p: Peripheral) { | ||
| 133 | this._peripherals.set(p.id, p); | ||
| 134 | } | ||
| 135 | |||
| 136 | private clearPeripherals() { | ||
| 137 | this._peripherals = new Map(); | ||
| 138 | } | ||
| 139 | |||
| 140 | private destroyListiners() { | ||
| 141 | for (const listener of this._listeners) { | ||
| 142 | if (listener) listener.remove(); | ||
| 143 | } | ||
| 144 | } | ||
| 145 | |||
| 146 | private setupListiners(events: Events) { | ||
| 147 | this.destroyListiners(); | ||
| 148 | this._listeners = [ | ||
| 149 | this._bleManagerEmitter.addListener( | ||
| 150 | 'BleManagerDiscoverPeripheral', | ||
| 151 | peripheral => { | ||
| 152 | this.addPeripheral(peripheral); | ||
| 153 | this.runEvent(events.bleManagerDiscoverPeripheral, peripheral); | ||
| 154 | }, | ||
| 155 | ), | ||
| 156 | this._bleManagerEmitter.addListener('BleManagerStopScan', () => | ||
| 157 | this.runEvent(events.bleManagerStopScan), | ||
| 158 | ), | ||
| 159 | this._bleManagerEmitter.addListener( | ||
| 160 | 'BleManagerDisconnectPeripheral', | ||
| 161 | event => this.runEvent(events.bleManagerDisconnectPeripheral, event), | ||
| 162 | ), | ||
| 163 | this._bleManagerEmitter.addListener( | ||
| 164 | 'BleManagerDidUpdateValueForCharacteristic', | ||
| 165 | event => | ||
| 166 | this.runEvent( | ||
| 167 | events.bleManagerDidUpdateValueForCharacteristic, | ||
| 168 | event, | ||
| 169 | ), | ||
| 170 | ), | ||
| 171 | this._bleManagerEmitter.addListener( | ||
| 172 | 'BleManagerConnectPeripheral', | ||
| 173 | event => this.runEvent(events.bleManagerConnectPeripheral, event), | ||
| 174 | ), | ||
| 175 | ]; | ||
| 176 | } | ||
| 177 | |||
| 178 | private runEvent( | ||
| 179 | event: CallableFunction | undefined, | ||
| 180 | ...optionalParams: any[] | ||
| 181 | ): void { | ||
| 182 | if (event) { | ||
| 183 | event(...optionalParams); | ||
| 184 | } | ||
| 185 | } | ||
| 186 | } | ||
