├── examples
└── ReactBLEScanner
│ ├── .watchmanconfig
│ ├── android
│ ├── app
│ │ ├── src
│ │ │ └── main
│ │ │ │ ├── res
│ │ │ │ ├── values
│ │ │ │ │ ├── strings.xml
│ │ │ │ │ └── styles.xml
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-ldpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ └── mipmap-xxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── java
│ │ │ │ └── com
│ │ │ │ │ └── polidea
│ │ │ │ │ └── sniffator
│ │ │ │ │ ├── MainActivity.java
│ │ │ │ │ └── MainApplication.java
│ │ │ │ └── AndroidManifest.xml
│ │ ├── Sniffator.keystore
│ │ ├── BUCK
│ │ ├── proguard-rules.pro
│ │ └── build.gradle
│ ├── keystores
│ │ ├── debug.keystore.properties
│ │ └── BUCK
│ ├── gradle
│ │ └── wrapper
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ ├── settings.gradle
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradlew.bat
│ └── gradlew
│ ├── ios
│ ├── ReactBLEScanner
│ │ ├── Images.xcassets
│ │ │ ├── Contents.json
│ │ │ ├── Icon-App.imageset
│ │ │ │ ├── Icon-App.png
│ │ │ │ ├── Icon-App@2x.png
│ │ │ │ ├── Icon-App@3x.png
│ │ │ │ └── Contents.json
│ │ │ └── AppIcon.appiconset
│ │ │ │ ├── Icon-App@2x.png
│ │ │ │ ├── Icon-App@3x.png
│ │ │ │ ├── Icon-Settings@2x.png
│ │ │ │ ├── Icon-Settings@3x.png
│ │ │ │ ├── Icon-Spotlight@2x.png
│ │ │ │ ├── Icon-Spotlight@3x.png
│ │ │ │ └── Contents.json
│ │ ├── AppDelegate.h
│ │ ├── main.m
│ │ ├── AppDelegate.m
│ │ ├── Info.plist
│ │ └── Base.lproj
│ │ │ └── LaunchScreen.xib
│ ├── ReactBLEScannerTests
│ │ ├── Info.plist
│ │ └── ReactBLEScannerTests.m
│ └── Sniffator.xcodeproj
│ │ └── xcshareddata
│ │ └── xcschemes
│ │ ├── Sniffator AdHoc.xcscheme
│ │ └── Sniffator.xcscheme
│ ├── tsconfig.json
│ ├── .buckconfig
│ ├── app
│ ├── view
│ │ ├── Style.js
│ │ ├── ButtonView.js
│ │ └── ImmutableListView.js
│ ├── scene
│ │ ├── Const.js
│ │ └── SceneReducer.js
│ ├── root
│ │ ├── Reducer.js
│ │ ├── ErrorComponent.js
│ │ └── RootComponent.js
│ ├── services
│ │ ├── ServicesComponent.js
│ │ └── ServiceView.js
│ ├── characteristics
│ │ ├── CharacteristicsComponent.js
│ │ ├── CharacteristicView.js
│ │ └── CharacteristicDetailsComponent.js
│ ├── scanning
│ │ ├── ScannedDeviceView.js
│ │ └── ScannedDevicesComponent.js
│ └── ble
│ │ ├── BleReducer.js
│ │ └── BleActions.js
│ ├── sync-ble-lib.sh
│ ├── install-ble-lib.sh
│ ├── .gitignore
│ ├── package.json
│ ├── index.ios.js
│ ├── .flowconfig
│ └── index.android.js
├── .babelrc
├── ios
├── BleClientManager
│ ├── Cartfile
│ ├── Cartfile.resolved
│ ├── BleClientManager.h
│ ├── BleEvent.swift
│ ├── Utils
│ │ ├── DisposableMap.swift
│ │ └── SafePromise.swift
│ ├── Info.plist
│ ├── BleUtils.swift
│ ├── BleClientManager.xcodeproj
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── BleClientManager.xcscheme
│ └── BleExtensions.swift
└── BleClient.h
├── docs
├── logo.png
├── appstore.png
├── googleplay.png
└── assets
│ ├── fonts
│ ├── EOT
│ │ ├── SourceCodePro-Bold.eot
│ │ └── SourceCodePro-Regular.eot
│ ├── OTF
│ │ ├── SourceCodePro-Bold.otf
│ │ └── SourceCodePro-Regular.otf
│ ├── TTF
│ │ ├── SourceCodePro-Bold.ttf
│ │ └── SourceCodePro-Regular.ttf
│ ├── WOFF
│ │ ├── OTF
│ │ │ ├── SourceCodePro-Bold.otf.woff
│ │ │ └── SourceCodePro-Regular.otf.woff
│ │ └── TTF
│ │ │ ├── SourceCodePro-Bold.ttf.woff
│ │ │ └── SourceCodePro-Regular.ttf.woff
│ ├── WOFF2
│ │ ├── OTF
│ │ │ ├── SourceCodePro-Bold.otf.woff2
│ │ │ └── SourceCodePro-Regular.otf.woff2
│ │ └── TTF
│ │ │ ├── SourceCodePro-Bold.ttf.woff2
│ │ │ └── SourceCodePro-Regular.ttf.woff2
│ ├── source-code-pro.css
│ └── LICENSE.txt
│ ├── bass-addons.css
│ ├── github.css
│ ├── style.css
│ └── site.js
├── android
├── src
│ ├── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── polidea
│ │ │ └── reactnativeble
│ │ │ ├── utils
│ │ │ ├── Base64Converter.java
│ │ │ ├── ReadableArrayConverter.java
│ │ │ ├── IdGenerator.java
│ │ │ ├── DisposableMap.java
│ │ │ ├── Constants.java
│ │ │ ├── SafePromise.java
│ │ │ ├── LogLevel.java
│ │ │ └── UUIDConverter.java
│ │ │ ├── Event.java
│ │ │ ├── converter
│ │ │ ├── JSObjectConverter.java
│ │ │ └── RxBleScanResultConverter.java
│ │ │ ├── exceptions
│ │ │ └── CannotMonitorCharacteristicException.java
│ │ │ ├── BlePackage.java
│ │ │ ├── errors
│ │ │ ├── Error.java
│ │ │ ├── BleError.java
│ │ │ └── ErrorConverter.java
│ │ │ ├── wrapper
│ │ │ ├── Service.java
│ │ │ ├── Device.java
│ │ │ └── Characteristic.java
│ │ │ └── advertisement
│ │ │ └── AdvertisementData.java
│ └── test
│ │ └── groovy
│ │ └── com
│ │ └── polidea
│ │ └── reactnativeble
│ │ └── utils
│ │ └── UUIDConverterSpec.groovy
└── build.gradle
├── jsconfig.json
├── index.js
├── .eslintrc.json
├── src
├── Utils.js
├── TypeDefinition.js
├── Characteristic.js
└── Service.js
├── .gitignore
├── documentation.yml
├── .npmignore
├── __tests__
├── Characteristic.js
├── Service.js
├── Utils.js
└── Device.js
├── package.json
├── .flowconfig
├── CHANGELOG.md
├── INTRO.md
└── README.md
/examples/ReactBLEScanner/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["react-native"]
3 | }
--------------------------------------------------------------------------------
/ios/BleClientManager/Cartfile:
--------------------------------------------------------------------------------
1 | Github "Polidea/RxBluetoothKit" "3.0.12"
2 |
--------------------------------------------------------------------------------
/docs/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-ble-plx/master/docs/logo.png
--------------------------------------------------------------------------------
/docs/appstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-ble-plx/master/docs/appstore.png
--------------------------------------------------------------------------------
/docs/googleplay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-ble-plx/master/docs/googleplay.png
--------------------------------------------------------------------------------
/ios/BleClientManager/Cartfile.resolved:
--------------------------------------------------------------------------------
1 | github "ReactiveX/RxSwift" "3.4.1"
2 | github "Polidea/RxBluetoothKit" "3.0.12"
3 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Sniffator
3 |
4 |
--------------------------------------------------------------------------------
/docs/assets/fonts/EOT/SourceCodePro-Bold.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-ble-plx/master/docs/assets/fonts/EOT/SourceCodePro-Bold.eot
--------------------------------------------------------------------------------
/docs/assets/fonts/OTF/SourceCodePro-Bold.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-ble-plx/master/docs/assets/fonts/OTF/SourceCodePro-Bold.otf
--------------------------------------------------------------------------------
/docs/assets/fonts/TTF/SourceCodePro-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-ble-plx/master/docs/assets/fonts/TTF/SourceCodePro-Bold.ttf
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/ios/ReactBLEScanner/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/docs/assets/fonts/EOT/SourceCodePro-Regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-ble-plx/master/docs/assets/fonts/EOT/SourceCodePro-Regular.eot
--------------------------------------------------------------------------------
/docs/assets/fonts/OTF/SourceCodePro-Regular.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-ble-plx/master/docs/assets/fonts/OTF/SourceCodePro-Regular.otf
--------------------------------------------------------------------------------
/docs/assets/fonts/TTF/SourceCodePro-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-ble-plx/master/docs/assets/fonts/TTF/SourceCodePro-Regular.ttf
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowJs": true
4 | },
5 | "exclude": [
6 | "node_modules"
7 | ]
8 | }
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/docs/assets/fonts/WOFF/OTF/SourceCodePro-Bold.otf.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-ble-plx/master/docs/assets/fonts/WOFF/OTF/SourceCodePro-Bold.otf.woff
--------------------------------------------------------------------------------
/docs/assets/fonts/WOFF/TTF/SourceCodePro-Bold.ttf.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-ble-plx/master/docs/assets/fonts/WOFF/TTF/SourceCodePro-Bold.ttf.woff
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/.buckconfig:
--------------------------------------------------------------------------------
1 |
2 | [android]
3 | target = Google Inc.:Google APIs:23
4 |
5 | [maven_repositories]
6 | central = https://repo1.maven.org/maven2
7 |
--------------------------------------------------------------------------------
/docs/assets/fonts/WOFF/OTF/SourceCodePro-Regular.otf.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-ble-plx/master/docs/assets/fonts/WOFF/OTF/SourceCodePro-Regular.otf.woff
--------------------------------------------------------------------------------
/docs/assets/fonts/WOFF/TTF/SourceCodePro-Regular.ttf.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-ble-plx/master/docs/assets/fonts/WOFF/TTF/SourceCodePro-Regular.ttf.woff
--------------------------------------------------------------------------------
/docs/assets/fonts/WOFF2/OTF/SourceCodePro-Bold.otf.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-ble-plx/master/docs/assets/fonts/WOFF2/OTF/SourceCodePro-Bold.otf.woff2
--------------------------------------------------------------------------------
/docs/assets/fonts/WOFF2/TTF/SourceCodePro-Bold.ttf.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-ble-plx/master/docs/assets/fonts/WOFF2/TTF/SourceCodePro-Bold.ttf.woff2
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/android/app/Sniffator.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-ble-plx/master/examples/ReactBLEScanner/android/app/Sniffator.keystore
--------------------------------------------------------------------------------
/docs/assets/fonts/WOFF2/OTF/SourceCodePro-Regular.otf.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-ble-plx/master/docs/assets/fonts/WOFF2/OTF/SourceCodePro-Regular.otf.woff2
--------------------------------------------------------------------------------
/docs/assets/fonts/WOFF2/TTF/SourceCodePro-Regular.ttf.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-ble-plx/master/docs/assets/fonts/WOFF2/TTF/SourceCodePro-Regular.ttf.woff2
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/android/keystores/debug.keystore.properties:
--------------------------------------------------------------------------------
1 | key.store=debug.keystore
2 | key.alias=androiddebugkey
3 | key.store.password=android
4 | key.alias.password=android
5 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowJs": true,
4 | "allowSyntheticDefaultImports": true
5 | },
6 | "exclude": [
7 | "node_modules"
8 | ]
9 | }
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-ble-plx/master/examples/ReactBLEScanner/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/android/keystores/BUCK:
--------------------------------------------------------------------------------
1 | keystore(
2 | name = 'debug',
3 | store = 'debug.keystore',
4 | properties = 'debug.keystore.properties',
5 | visibility = [
6 | 'PUBLIC',
7 | ],
8 | )
9 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-ble-plx/master/examples/ReactBLEScanner/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/android/app/src/main/res/mipmap-ldpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-ble-plx/master/examples/ReactBLEScanner/android/app/src/main/res/mipmap-ldpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-ble-plx/master/examples/ReactBLEScanner/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-ble-plx/master/examples/ReactBLEScanner/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-ble-plx/master/examples/ReactBLEScanner/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/ios/ReactBLEScanner/Images.xcassets/Icon-App.imageset/Icon-App.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-ble-plx/master/examples/ReactBLEScanner/ios/ReactBLEScanner/Images.xcassets/Icon-App.imageset/Icon-App.png
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/ios/ReactBLEScanner/Images.xcassets/Icon-App.imageset/Icon-App@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-ble-plx/master/examples/ReactBLEScanner/ios/ReactBLEScanner/Images.xcassets/Icon-App.imageset/Icon-App@2x.png
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/ios/ReactBLEScanner/Images.xcassets/Icon-App.imageset/Icon-App@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-ble-plx/master/examples/ReactBLEScanner/ios/ReactBLEScanner/Images.xcassets/Icon-App.imageset/Icon-App@3x.png
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/ios/ReactBLEScanner/Images.xcassets/AppIcon.appiconset/Icon-App@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-ble-plx/master/examples/ReactBLEScanner/ios/ReactBLEScanner/Images.xcassets/AppIcon.appiconset/Icon-App@2x.png
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/ios/ReactBLEScanner/Images.xcassets/AppIcon.appiconset/Icon-App@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-ble-plx/master/examples/ReactBLEScanner/ios/ReactBLEScanner/Images.xcassets/AppIcon.appiconset/Icon-App@3x.png
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/ios/ReactBLEScanner/Images.xcassets/AppIcon.appiconset/Icon-Settings@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-ble-plx/master/examples/ReactBLEScanner/ios/ReactBLEScanner/Images.xcassets/AppIcon.appiconset/Icon-Settings@2x.png
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/ios/ReactBLEScanner/Images.xcassets/AppIcon.appiconset/Icon-Settings@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-ble-plx/master/examples/ReactBLEScanner/ios/ReactBLEScanner/Images.xcassets/AppIcon.appiconset/Icon-Settings@3x.png
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/ios/ReactBLEScanner/Images.xcassets/AppIcon.appiconset/Icon-Spotlight@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-ble-plx/master/examples/ReactBLEScanner/ios/ReactBLEScanner/Images.xcassets/AppIcon.appiconset/Icon-Spotlight@2x.png
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/ios/ReactBLEScanner/Images.xcassets/AppIcon.appiconset/Icon-Spotlight@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-ble-plx/master/examples/ReactBLEScanner/ios/ReactBLEScanner/Images.xcassets/AppIcon.appiconset/Icon-Spotlight@3x.png
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/app/view/Style.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native'
2 |
3 | export default style = StyleSheet.create({
4 | component: {
5 | backgroundColor: 'white',
6 | padding: 20,
7 | paddingTop: 70,
8 | flex: 1,
9 | }
10 | });
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/app/scene/Const.js:
--------------------------------------------------------------------------------
1 |
2 | export const DEVICES_SCENE = 'DEVICES_SCENE';
3 | export const SERVICES_SCENE = 'SERVICES_SCENE';
4 | export const CHARACTERISTICS_SCENE = 'CHARACTERISTICS_SCENE';
5 | export const CHARACTERISTIC_DETAILS_SCENE = 'CHARACTERISTIC_DETAILS_SCENE';
--------------------------------------------------------------------------------
/docs/assets/bass-addons.css:
--------------------------------------------------------------------------------
1 | .input {
2 | font-family: inherit;
3 | display: block;
4 | width: 100%;
5 | height: 2rem;
6 | padding: .5rem;
7 | margin-bottom: 1rem;
8 | border: 1px solid #ccc;
9 | font-size: .875rem;
10 | border-radius: 3px;
11 | box-sizing: border-box;
12 | }
13 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Aug 03 10:57:28 CEST 2016
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
7 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/app/root/Reducer.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import { combineReducers } from 'redux-immutable'
4 | import bleReducer from '../ble/BleReducer'
5 | import sceneReducer from '../scene/SceneReducer'
6 |
7 | export default combineReducers({
8 | ble: bleReducer,
9 | route: sceneReducer
10 | })
11 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/sync-ble-lib.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | rsync -a --delete --progress ./node_modules/react-native-ble-plx/ ../../ \
3 | --exclude examples \
4 | --exclude '.*' \
5 | --exclude ios/BleClientManager/Carthage \
6 | --exclude android/build \
7 | --exclude android/react-native-ble-plx.iml \
8 | --exclude README.md
9 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/install-ble-lib.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | rsync -a --delete --progress ../../ ./node_modules/react-native-ble-plx \
3 | --exclude examples \
4 | --exclude '.*' \
5 | --exclude ios/BleClientManager/Carthage \
6 | --exclude android/build \
7 | --exclude android/react-native-ble-plx.iml \
8 | --exclude README.md
9 |
10 |
--------------------------------------------------------------------------------
/ios/BleClient.h:
--------------------------------------------------------------------------------
1 | //
2 | // BleClient.h
3 | // BleClient
4 | //
5 | // Created by Przemysław Lenart on 27/07/16.
6 | // Copyright © 2016 Polidea. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "RCTBridgeModule.h"
11 | #import "RCTEventEmitter.h"
12 |
13 | @interface BleModule : RCTEventEmitter
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'ReactBLEScanner'
2 |
3 | include ':app'
4 |
5 | include ':react-native-ble-plx'
6 | project(':react-native-ble-plx').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-ble-plx/android')
7 |
8 | include ':RNPermissionsModule'
9 | project(':RNPermissionsModule').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-android-permissions/android')
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/reactnativeble/utils/Base64Converter.java:
--------------------------------------------------------------------------------
1 | package com.polidea.reactnativeble.utils;
2 |
3 | import android.util.Base64;
4 |
5 | public class Base64Converter {
6 | public static String encode(byte[] bytes) {
7 | return Base64.encodeToString(bytes, Base64.NO_WRAP);
8 | }
9 | public static byte[] decode(String base64) {
10 | return Base64.decode(base64, Base64.NO_WRAP);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/reactnativeble/Event.java:
--------------------------------------------------------------------------------
1 | package com.polidea.reactnativeble;
2 |
3 | public enum Event {
4 |
5 | ScanEvent("ScanEvent"),
6 | ReadEvent("ReadEvent"),
7 | StateChangeEvent("StateChangeEvent"),
8 | RestoreStateEvent("RestoreStateEvent"),
9 | DisconnectionEvent("DisconnectionEvent");
10 |
11 | public String name;
12 |
13 | Event(String name) {
14 | this.name = name;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/app/scene/SceneReducer.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import { Map } from 'immutable'
4 | import { ActionConst } from 'react-native-router-flux'
5 | import * as SceneConst from './Const.js'
6 |
7 | const defaultState = Map({
8 | 'state' : SceneConst.DEVICES_SCENE
9 | })
10 |
11 | export default function (state = defaultState, action) {
12 | switch(action.type) {
13 | case ActionConst.FOCUS:
14 | return state.set('state', action.scene.sceneKey);
15 | }
16 | return state;
17 | }
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/reactnativeble/utils/ReadableArrayConverter.java:
--------------------------------------------------------------------------------
1 | package com.polidea.reactnativeble.utils;
2 |
3 | import com.facebook.react.bridge.ReadableArray;
4 |
5 | public class ReadableArrayConverter {
6 | public static String[] toStringArray(ReadableArray readableArray) {
7 | String[] stringArray = new String[readableArray.size()];
8 | for (int i = 0; i < readableArray.size(); ++i) {
9 | stringArray[i] = readableArray.getString(i);
10 | }
11 | return stringArray;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | export { BleManager } from './src/BleManager'
4 | export { Device } from './src/Device'
5 | export { Service } from './src/Service'
6 | export { Characteristic } from './src/Characteristic'
7 | export { fullUUID } from './src/Utils'
8 | export { State, LogLevel } from './src/TypeDefinition'
9 |
10 | export type {
11 | Subscription,
12 | DeviceId,
13 | UUID,
14 | TransactionId,
15 | Base64,
16 | ScanOptions,
17 | ConnectionOptions,
18 | BleManagerOptions,
19 | BleRestoredState
20 | } from './src/TypeDefinition'
21 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/ios/ReactBLEScanner/Images.xcassets/Icon-App.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Icon-App.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Icon-App@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Icon-App@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/ios/ReactBLEScanner/AppDelegate.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2015-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 |
12 | @interface AppDelegate : UIResponder
13 |
14 | @property (nonatomic, strong) UIWindow *window;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/ios/BleClientManager/BleClientManager.h:
--------------------------------------------------------------------------------
1 | //
2 | // BleClientManager.h
3 | //
4 | // Created by Przemysław Lenart on 27/07/16.
5 | //
6 |
7 | #import
8 |
9 | //! Project version number for BleClientManager.
10 | FOUNDATION_EXPORT double BleClientManagerVersionNumber;
11 |
12 | //! Project version string for BleClientManager.
13 | FOUNDATION_EXPORT const unsigned char BleClientManagerVersionString[];
14 |
15 | // In this header, you should import all the public headers of your framework using statements like #import
16 |
17 |
18 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "extends": [
4 | "eslint:recommended",
5 | "plugin:react/recommended",
6 | "plugin:flowtype/recommended"
7 | ],
8 | "plugins": [
9 | "react",
10 | "import",
11 | "flowtype",
12 | "jest"
13 | ],
14 | "rules": {
15 | "indent": [
16 | "error",
17 | 2,
18 | {
19 | "SwitchCase": 1
20 | }
21 | ],
22 | "semi": [
23 | "error",
24 | "never"
25 | ]
26 | },
27 | "env": {
28 | "node": true,
29 | "es6": true,
30 | "jest": true
31 | }
32 | }
--------------------------------------------------------------------------------
/src/Utils.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | 'use strict'
3 |
4 | import type { UUID } from './TypeDefinition'
5 |
6 | /**
7 | * Converts UUID to full 128bit, lowercase format which should be used to compare UUID values.
8 | *
9 | * @param {UUID} uuid 16bit, 32bit or 128bit UUID.
10 | * @returns {UUID} 128bit lowercase UUID.
11 | */
12 | export function fullUUID(uuid: UUID): UUID {
13 | if (uuid.length === 4) return '0000' + uuid.toLowerCase() + '-0000-1000-8000-00805f9b34fb'
14 | if (uuid.length === 8) return uuid.toLowerCase() + '-0000-1000-8000-00805f9b34fb'
15 | return uuid.toLowerCase()
16 | }
17 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/reactnativeble/converter/JSObjectConverter.java:
--------------------------------------------------------------------------------
1 | package com.polidea.reactnativeble.converter;
2 |
3 | import com.facebook.react.bridge.Arguments;
4 | import com.facebook.react.bridge.WritableArray;
5 | import com.facebook.react.bridge.WritableMap;
6 |
7 | abstract class JSObjectConverter {
8 |
9 | abstract public WritableMap toJSObject(T value);
10 |
11 | public WritableArray toJSCallback(T value) {
12 | WritableArray array = Arguments.createArray();
13 | array.pushNull();
14 | array.pushMap(toJSObject(value));
15 | return array;
16 | }
17 | }
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/ios/ReactBLEScanner/main.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2015-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 |
12 | #import "AppDelegate.h"
13 |
14 | int main(int argc, char * argv[]) {
15 | @autoreleasepool {
16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/reactnativeble/exceptions/CannotMonitorCharacteristicException.java:
--------------------------------------------------------------------------------
1 | package com.polidea.reactnativeble.exceptions;
2 |
3 | import android.bluetooth.BluetoothGattCharacteristic;
4 |
5 | public class CannotMonitorCharacteristicException extends RuntimeException {
6 | private BluetoothGattCharacteristic characteristic;
7 |
8 | public CannotMonitorCharacteristicException(BluetoothGattCharacteristic characteristic) {
9 | this.characteristic = characteristic;
10 | }
11 |
12 | public BluetoothGattCharacteristic getCharacteristic() {
13 | return characteristic;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 | project.xcworkspace
24 |
25 | # Android/IJ
26 | #
27 | *.iml
28 | .idea
29 | .gradle
30 | local.properties
31 |
32 | # node.js
33 | #
34 | node_modules/
35 | npm-debug.log
36 |
37 | # BUCK
38 | buck-out/
39 | \.buckd/
40 | android/app/libs
41 | android/keystores/debug.keystore
42 |
--------------------------------------------------------------------------------
/ios/BleClientManager/BleEvent.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BleEvent.swift
3 | //
4 | // Created by Przemysław Lenart on 25/07/16.
5 | //
6 |
7 | import Foundation
8 |
9 | @objc
10 | public class BleEvent: NSObject {
11 | static public let scanEvent = "ScanEvent"
12 | static public let readEvent = "ReadEvent"
13 | static public let stateChangeEvent = "StateChangeEvent"
14 | static public let restoreStateEvent = "RestoreStateEvent"
15 | static public let disconnectionEvent = "DisconnectionEvent"
16 |
17 | static public let events = [
18 | scanEvent,
19 | readEvent,
20 | stateChangeEvent,
21 | restoreStateEvent,
22 | disconnectionEvent
23 | ]
24 | }
25 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/reactnativeble/utils/IdGenerator.java:
--------------------------------------------------------------------------------
1 | package com.polidea.reactnativeble.utils;
2 |
3 | import android.util.Pair;
4 | import java.util.HashMap;
5 | import java.util.UUID;
6 |
7 | public class IdGenerator {
8 | private static HashMap, Integer> idMap = new HashMap<>();
9 | private static int nextKey = 0;
10 |
11 | public static int getIdForKey(Pair keyPair) {
12 | Integer id = idMap.get(keyPair);
13 | if (id != null) {
14 | return id;
15 | }
16 | idMap.put(keyPair, ++nextKey);
17 | return nextKey;
18 | }
19 |
20 | public static void clear() {
21 | idMap.clear();
22 | nextKey = 0;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 | project.xcworkspace
24 |
25 | # Android/IJ
26 | #
27 | .idea
28 | .gradle
29 | gradlew
30 | *.iml
31 | *.swp
32 | *.bat
33 | local.properties
34 | android/gradle/wrapper
35 |
36 | # node.js
37 | #
38 | node_modules/
39 | npm-debug.log.*
40 |
41 | # BUCK
42 | buck-out/
43 | \.buckd/
44 | android/app/libs
45 | android/keystores/debug.keystore
46 |
47 | # iOS
48 | ios/Pods
49 | ios/Podfile.lock
50 | Carthage
51 |
52 | # IDE
53 | .vscode/
54 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/android/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.1.2'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | mavenLocal()
18 | jcenter()
19 | maven {
20 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
21 | url "$rootDir/../node_modules/react-native/android"
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/documentation.yml:
--------------------------------------------------------------------------------
1 | toc:
2 | - name: Introduction
3 | file: INTRO.md
4 | - name: Main Classes
5 | description: |
6 | Classes described below are main building blocks for your BLE support. They
7 | are presented in order which aligns them with usage.
8 | - BleManager
9 | - Device
10 | - Service
11 | - Characteristic
12 | - name: Utils
13 | description: |
14 | Utility functions and classes.
15 | - fullUUID
16 | - name: Flow Types
17 | description: |
18 | All Flow aliases and Flow types used in this library.
19 | - LogLevel
20 | - State
21 | - BleManagerOptions
22 | - BleRestoredState
23 | - ScanOptions
24 | - ConnectionOptions
25 | - DeviceId
26 | - Identifier
27 | - UUID
28 | - TransactionId
29 | - Subscription
30 | - Base64
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ReactBLEScanner",
3 | "version": "0.0.1",
4 | "private": true,
5 | "scripts": {
6 | "start": "node node_modules/react-native/local-cli/cli.js start"
7 | },
8 | "dependencies": {
9 | "react": "15.2.1",
10 | "react-addons-pure-render-mixin": "15.2.1",
11 | "react-native": "0.30.0",
12 | "redux": "3.5.2",
13 | "react-redux": "4.4.5",
14 | "redux-logger": "2.6.1",
15 | "react-native-router-flux": "3.32.0",
16 | "immutable": "3.8.1",
17 | "redux-immutable": "3.0.6",
18 | "react-immutable-proptypes": "2.0.0",
19 | "react-native-button": "1.6.0",
20 | "react-native-android-permissions": "0.0.6",
21 | "buffer": "4.7.1",
22 | "react-native-ble-plx": "Polidea/react-native-ble-plx"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/ios/BleClientManager/Utils/DisposableMap.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DisposableMap.swift
3 | // BleClientManager
4 | //
5 | // Created by Przemysław Lenart on 25/10/16.
6 | // Copyright © 2016 Polidea. All rights reserved.
7 | //
8 |
9 | import RxSwift
10 |
11 | class DisposableMap {
12 | fileprivate var disposables = Dictionary()
13 |
14 | func replaceDisposable(_ key: T, disposable: Disposable?) {
15 | disposables[key]?.dispose()
16 | disposables[key] = disposable
17 | }
18 |
19 | func removeDisposable(_ key: T) {
20 | replaceDisposable(key, disposable: nil)
21 | }
22 |
23 | func dispose() {
24 | disposables.forEach {
25 | (_, disposable) in
26 | disposable.dispose()
27 | }
28 | disposables.removeAll()
29 | }
30 |
31 | deinit {
32 | dispose()
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/android/app/src/main/java/com/polidea/sniffator/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.polidea.sniffator;
2 |
3 | import com.burnweb.rnpermissions.RNPermissionsPackage;
4 | import com.facebook.react.ReactActivity;
5 |
6 | public class MainActivity extends ReactActivity {
7 |
8 | /**
9 | * Returns the name of the main component registered from JavaScript.
10 | * This is used to schedule rendering of the component.
11 | */
12 | @Override
13 | protected String getMainComponentName() {
14 | return "ReactBLEScanner";
15 | }
16 |
17 | @Override
18 | public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
19 | RNPermissionsPackage.onRequestPermissionsResult(requestCode, permissions, grantResults);
20 | super.onRequestPermissionsResult(requestCode, permissions, grantResults);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/ios/ReactBLEScannerTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | jcenter()
4 | }
5 | dependencies {
6 | classpath 'com.android.tools.build:gradle:2.1.2'
7 | classpath 'org.codehaus.groovy:groovy-android-gradle-plugin:1.1.0'
8 | }
9 | }
10 |
11 | apply plugin: 'com.android.library'
12 | apply plugin: 'groovyx.android'
13 |
14 | android {
15 | compileSdkVersion 23
16 | buildToolsVersion "23.0.3"
17 |
18 | defaultConfig {
19 | minSdkVersion 18
20 | targetSdkVersion 23
21 | versionCode 1
22 | versionName "1.0"
23 | }
24 | lintOptions {
25 | abortOnError false
26 | }
27 | }
28 |
29 | repositories {
30 | mavenCentral()
31 | }
32 |
33 | dependencies {
34 | compile "com.facebook.react:react-native:0.20.1"
35 | compile "com.polidea.rxandroidble:rxandroidble:1.3.0"
36 |
37 | testCompile 'org.robospock:robospock:1.0.1'
38 | }
--------------------------------------------------------------------------------
/ios/BleClientManager/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # Npm specific
2 | examples
3 | .git
4 | .gitignore
5 |
6 | # OSX
7 | #
8 | .DS_Store
9 |
10 | # Xcode
11 | #
12 | build/
13 | *.pbxuser
14 | !default.pbxuser
15 | *.mode1v3
16 | !default.mode1v3
17 | *.mode2v3
18 | !default.mode2v3
19 | *.perspectivev3
20 | !default.perspectivev3
21 | xcuserdata
22 | *.xccheckout
23 | *.moved-aside
24 | DerivedData
25 | *.hmap
26 | *.ipa
27 | *.xcuserstate
28 | project.xcworkspace
29 |
30 | # Android/IJ
31 | #
32 | .idea
33 | .gradle
34 | gradlew
35 | *.iml
36 | *.swp
37 | *.bat
38 | local.properties
39 | android/gradle/wrapper
40 |
41 | # node.js
42 | #
43 | node_modules/
44 | npm-debug.log.*
45 |
46 | # BUCK
47 | buck-out/
48 | \.buckd/
49 | android/app/libs
50 | android/keystores/debug.keystore
51 |
52 | # iOS
53 | ios/Pods
54 | ios/Podfile.lock
55 | Carthage
56 |
57 | # IDE
58 | .vscode/
59 |
60 | # Graphics
61 | *.png
62 |
63 | # Documentation
64 | docs/
65 |
66 | # Tests
67 | __tests__/
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 |
20 | android.useDeprecatedNdk=true
21 |
--------------------------------------------------------------------------------
/docs/assets/fonts/source-code-pro.css:
--------------------------------------------------------------------------------
1 | @font-face{
2 | font-family: 'Source Code Pro';
3 | font-weight: 400;
4 | font-style: normal;
5 | font-stretch: normal;
6 | src: url('EOT/SourceCodePro-Regular.eot') format('embedded-opentype'),
7 | url('WOFF2/TTF/SourceCodePro-Regular.ttf.woff2') format('woff2'),
8 | url('WOFF/OTF/SourceCodePro-Regular.otf.woff') format('woff'),
9 | url('OTF/SourceCodePro-Regular.otf') format('opentype'),
10 | url('TTF/SourceCodePro-Regular.ttf') format('truetype');
11 | }
12 |
13 | @font-face{
14 | font-family: 'Source Code Pro';
15 | font-weight: 700;
16 | font-style: normal;
17 | font-stretch: normal;
18 | src: url('EOT/SourceCodePro-Bold.eot') format('embedded-opentype'),
19 | url('WOFF2/TTF/SourceCodePro-Bold.ttf.woff2') format('woff2'),
20 | url('WOFF/OTF/SourceCodePro-Bold.otf.woff') format('woff'),
21 | url('OTF/SourceCodePro-Bold.otf') format('opentype'),
22 | url('TTF/SourceCodePro-Bold.ttf') format('truetype');
23 | }
24 |
--------------------------------------------------------------------------------
/ios/BleClientManager/Utils/SafePromise.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SafePromise.swift
3 | // BleClientManager
4 | //
5 | // Created by Przemysław Lenart on 25/10/16.
6 | // Copyright © 2016 Polidea. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public typealias Resolve = (Any?) -> ()
12 | public typealias Reject = (String?, String?, NSError?) -> ()
13 |
14 | class SafePromise {
15 | let resolveFunc: Resolve
16 | let rejectFunc: Reject
17 | var finished = false
18 |
19 | init(resolve: @escaping Resolve, reject: @escaping Reject) {
20 | self.resolveFunc = resolve
21 | self.rejectFunc = reject
22 | }
23 |
24 | func resolve(_ value: Any? = nil) {
25 | if (!finished) {
26 | self.resolveFunc(value)
27 | finished = true
28 | }
29 | }
30 |
31 | func reject(code: String? = nil, message: String? = nil, error: NSError? = nil) {
32 | if (!finished) {
33 | rejectFunc(code, message, error)
34 | finished = true
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/ios/ReactBLEScanner/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "29x29",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-Settings@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "29x29",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-Settings@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "40x40",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-Spotlight@2x.png",
19 | "scale" : "2x"
20 | },
21 | {
22 | "size" : "40x40",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-Spotlight@3x.png",
25 | "scale" : "3x"
26 | },
27 | {
28 | "size" : "60x60",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App@2x.png",
31 | "scale" : "2x"
32 | },
33 | {
34 | "size" : "60x60",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App@3x.png",
37 | "scale" : "3x"
38 | }
39 | ],
40 | "info" : {
41 | "version" : 1,
42 | "author" : "xcode"
43 | }
44 | }
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/reactnativeble/BlePackage.java:
--------------------------------------------------------------------------------
1 | package com.polidea.reactnativeble;
2 |
3 | import com.facebook.react.ReactPackage;
4 | import com.facebook.react.bridge.JavaScriptModule;
5 | import com.facebook.react.bridge.NativeModule;
6 | import com.facebook.react.bridge.ReactApplicationContext;
7 | import com.facebook.react.uimanager.ViewManager;
8 | import java.util.ArrayList;
9 | import java.util.Collections;
10 | import java.util.List;
11 |
12 | public class BlePackage implements ReactPackage {
13 |
14 | @Override
15 | public List> createJSModules() {
16 | return Collections.emptyList();
17 | }
18 |
19 | @Override
20 | public List createViewManagers(ReactApplicationContext reactContext) {
21 | return Collections.emptyList();
22 | }
23 |
24 | @Override
25 | public List createNativeModules(ReactApplicationContext reactContext) {
26 | List modules = new ArrayList<>();
27 | modules.add(new BleModule(reactContext));
28 | return modules;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/app/root/ErrorComponent.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, { Component } from 'react';
4 | import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
5 | import { connect } from 'react-redux';
6 | import * as ble from '../ble/BleActions';
7 |
8 | class ErrorComponent extends Component {
9 | render() {
10 | const lastError = this.props.errorMessages.last()
11 |
12 | if (!lastError) {
13 | return null
14 | }
15 |
16 | return (
17 |
18 |
19 | {lastError}
20 |
21 |
22 | )
23 | }
24 | }
25 |
26 | const styles = StyleSheet.create({
27 | container: {
28 | padding: 20,
29 | backgroundColor: 'red',
30 | },
31 | message: {
32 | color: 'white',
33 | fontWeight: 'bold'
34 | }
35 | });
36 |
37 | export default connect(
38 | state => ({
39 | errorMessages: state.getIn(['ble', 'errors'])
40 | }),
41 | {
42 | pop: ble.popError
43 | }
44 | )(ErrorComponent)
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/app/view/ButtonView.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, { Component, PropTypes } from 'react';
4 | import { StyleSheet } from 'react-native';
5 | import Button from 'react-native-button'
6 |
7 | const ButtonView = ({onClick, disabled, color, text}) => {
8 | var style = [styles.containerStyle];
9 | style.push({backgroundColor: color})
10 | if (disabled) style.push(styles.buttonDisabled)
11 |
12 | return (
13 |
19 | )
20 | }
21 |
22 | ButtonView.propTypes = {
23 | onClick: PropTypes.func.isRequired,
24 | disabled: PropTypes.bool.isRequired,
25 | color: PropTypes.string.isRequired,
26 | text: PropTypes.string.isRequired,
27 | }
28 |
29 | var styles = StyleSheet.create({
30 | containerStyle: {
31 | padding: 10,
32 | height: 45,
33 | overflow: 'hidden',
34 | borderRadius: 10,
35 | borderWidth: 1,
36 | },
37 | buttonDisabled: {
38 | backgroundColor: '#ede9eb'
39 | }
40 | });
41 |
42 | export default ButtonView
43 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/index.ios.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, { Component } from 'react';
4 | import { View, AppRegistry } from 'react-native';
5 | import { applyMiddleware, createStore } from 'redux';
6 | import { Provider } from 'react-redux';
7 | import createLogger from 'redux-logger';
8 |
9 | import RootComponent from './app/root/RootComponent';
10 | import ErrorComponent from './app/root/ErrorComponent';
11 | import BleComponent from './app/ble/BleComponent';
12 | import reducer from './app/root/Reducer';
13 |
14 | import { Iterable } from 'immutable';
15 |
16 | const stateTransformer = (state) => {
17 | if (Iterable.isIterable(state)) {
18 | return state.toJS()
19 | } else {
20 | return state;
21 | }
22 | };
23 |
24 | const logger = createLogger({ stateTransformer });
25 | const store = createStore(reducer)
26 |
27 | class ReactBLEScanner extends Component {
28 | render() {
29 | return (
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | );
38 | }
39 | }
40 |
41 | AppRegistry.registerComponent('ReactBLEScanner', () => ReactBLEScanner);
42 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/android/app/src/main/java/com/polidea/sniffator/MainApplication.java:
--------------------------------------------------------------------------------
1 | package com.polidea.sniffator;
2 |
3 | import android.app.Application;
4 |
5 | import com.burnweb.rnpermissions.RNPermissionsPackage;
6 | import com.facebook.react.ReactApplication;
7 | import com.facebook.react.ReactNativeHost;
8 | import com.facebook.react.ReactPackage;
9 | import com.facebook.react.shell.MainReactPackage;
10 | import com.polidea.reactnativeble.BlePackage;
11 |
12 | import java.util.Arrays;
13 | import java.util.List;
14 |
15 | public class MainApplication extends Application implements ReactApplication {
16 |
17 | private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
18 | @Override
19 | protected boolean getUseDeveloperSupport() {
20 | return BuildConfig.DEBUG;
21 | }
22 |
23 | @Override
24 | protected List getPackages() {
25 | return Arrays.asList(
26 | new MainReactPackage(),
27 | new BlePackage(),
28 | new RNPermissionsPackage()
29 | );
30 | }
31 | };
32 |
33 | @Override
34 | public ReactNativeHost getReactNativeHost() {
35 | return mReactNativeHost;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/__tests__/Characteristic.js:
--------------------------------------------------------------------------------
1 | jest.mock('../src/BleManager')
2 | import { BleManager } from '../src/BleManager'
3 | import { Characteristic } from '../src/Characteristic'
4 |
5 | describe("Test if Characteristic is properly calling BleManager's utility function: ", () => {
6 | const bleManager = new BleManager()
7 | const characteristic = new Characteristic(
8 | { id: 'cId', uuid: 'uuid', serviceUUID: 'serviceUUID', deviceID: 'deviceId' },
9 | bleManager
10 | )
11 |
12 | test('read', async () => {
13 | await characteristic.read('id')
14 | expect(bleManager._readCharacteristic).toBeCalledWith('cId', 'id')
15 | })
16 |
17 | test('writeWithResponse', async () => {
18 | await characteristic.writeWithResponse('value', 'id')
19 | expect(bleManager._writeCharacteristicWithResponse).toBeCalledWith('cId', 'value', 'id')
20 | })
21 |
22 | test('writeWithoutResponse', async () => {
23 | await characteristic.writeWithoutResponse('value', 'id')
24 | expect(bleManager._writeCharacteristicWithoutResponse).toBeCalledWith('cId', 'value', 'id')
25 | })
26 |
27 | test('monitor', async () => {
28 | const listener = jest.fn()
29 | await characteristic.monitor(listener, 'id')
30 | expect(bleManager._monitorCharacteristic).toBeCalledWith('cId', listener, 'id')
31 | })
32 | })
33 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/.flowconfig:
--------------------------------------------------------------------------------
1 | [ignore]
2 |
3 | # We fork some components by platform.
4 | .*/*.android.js
5 |
6 | # Ignore templates with `@flow` in header
7 | .*/local-cli/generator.*
8 |
9 | # Ignore malformed json
10 | .*/node_modules/y18n/test/.*\.json
11 |
12 | [include]
13 |
14 | [libs]
15 | node_modules/react-native/Libraries/react-native/react-native-interface.js
16 | node_modules/react-native/flow
17 | flow/
18 |
19 | [options]
20 | module.system=haste
21 |
22 | esproposal.class_static_fields=enable
23 | esproposal.class_instance_fields=enable
24 |
25 | experimental.strict_type_args=true
26 |
27 | munge_underscores=true
28 |
29 | module.name_mapper='^image![a-zA-Z0-9$_-]+$' -> 'GlobalImageStub'
30 | module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
31 |
32 | suppress_type=$FlowIssue
33 | suppress_type=$FlowFixMe
34 | suppress_type=$FixMe
35 |
36 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(2[0-7]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
37 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(2[0-7]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
38 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
39 |
40 | [version]
41 | ^0.27.0
42 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/reactnativeble/utils/DisposableMap.java:
--------------------------------------------------------------------------------
1 | package com.polidea.reactnativeble.utils;
2 |
3 | import java.util.HashMap;
4 | import java.util.Iterator;
5 | import java.util.Map;
6 |
7 | import rx.Subscription;
8 |
9 | public class DisposableMap {
10 |
11 | final private Map subscriptions = new HashMap<>();
12 |
13 | public void replaceSubscription(String key, Subscription subscription) {
14 | Subscription oldSubscription = subscriptions.put(key, subscription);
15 | if (oldSubscription != null && !oldSubscription.isUnsubscribed()) {
16 | oldSubscription.unsubscribe();
17 | }
18 | }
19 |
20 | public boolean removeSubscription(String key) {
21 | Subscription subscription = subscriptions.remove(key);
22 | if (subscription == null) return false;
23 | if (!subscription.isUnsubscribed()) {
24 | subscription.unsubscribe();
25 | }
26 | return true;
27 | }
28 |
29 | public void removeAllSubscriptions() {
30 | Iterator> it = subscriptions.entrySet().iterator();
31 | while (it.hasNext()) {
32 | Subscription subscription = it.next().getValue();
33 | it.remove();
34 | if (!subscription.isUnsubscribed()) {
35 | subscription.unsubscribe();
36 | }
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
14 |
15 |
21 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/app/view/ImmutableListView.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, { Component, PropTypes } from 'react';
4 | import {
5 | View,
6 | ListView,
7 | ListViewDataSource
8 | } from 'react-native';
9 | import Immutable from 'immutable'
10 | import ImmutablePropTypes from 'react-immutable-proptypes'
11 |
12 | export default class ImmutableListView extends Component {
13 |
14 | constructor(props) {
15 | super(props)
16 | const ds = new ListView.DataSource({
17 | rowHasChanged: (r1, r2) => !Immutable.is(r1, r2)
18 | });
19 |
20 | this.state = { dataSource: ds.cloneWithRows(this.props.data.toObject()) };
21 | }
22 |
23 | static propTypes = {
24 | onRenderCell: PropTypes.func.isRequired,
25 | data: ImmutablePropTypes.iterable.isRequired
26 | }
27 |
28 | componentWillReceiveProps(nextProps) {
29 | this.setState({
30 | dataSource: this.state.dataSource.cloneWithRows(nextProps.data.toObject())
31 | })
32 | }
33 |
34 | _renderSeparator(section, row, adjacentRowHighlighted) {
35 | return (
36 |
40 | );
41 | }
42 |
43 | render() {
44 | return (
45 |
51 | );
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/reactnativeble/errors/Error.java:
--------------------------------------------------------------------------------
1 | package com.polidea.reactnativeble.errors;
2 |
3 | import com.facebook.react.bridge.Arguments;
4 | import com.facebook.react.bridge.Promise;
5 | import com.facebook.react.bridge.ReadableArray;
6 | import com.facebook.react.bridge.WritableArray;
7 | import com.facebook.react.bridge.WritableMap;
8 |
9 | public class Error {
10 | private int code;
11 | private String message;
12 | private boolean isCancelled;
13 |
14 | public Error(String message, int code, boolean isCancelled) {
15 | this.code = code;
16 | this.message = message;
17 | this.isCancelled = isCancelled;
18 | }
19 |
20 | public Error(String message, int code) {
21 | this.code = code;
22 | this.message = message;
23 | this.isCancelled = false;
24 | }
25 |
26 | public WritableMap toJS() {
27 | WritableMap error = Arguments.createMap();
28 | error.putInt("code", code);
29 | error.putString("message", message);
30 | if (isCancelled) {
31 | error.putBoolean("isCancelled", true);
32 | }
33 | return error;
34 | }
35 |
36 | public ReadableArray toJSCallback() {
37 | WritableArray array = Arguments.createArray();
38 | array.pushMap(toJS());
39 | array.pushNull();
40 | return array;
41 | }
42 |
43 | public void reject(Promise promise) {
44 | promise.reject(Integer.toString(code), message);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/reactnativeble/utils/Constants.java:
--------------------------------------------------------------------------------
1 | package com.polidea.reactnativeble.utils;
2 |
3 |
4 | import android.support.annotation.StringDef;
5 | import java.lang.annotation.Retention;
6 | import java.lang.annotation.RetentionPolicy;
7 |
8 | public interface Constants {
9 |
10 | @StringDef({
11 | BluetoothState.UNKNOWN,
12 | BluetoothState.RESETTING,
13 | BluetoothState.UNSUPPORTED,
14 | BluetoothState.UNAUTHORIZED,
15 | BluetoothState.POWERED_OFF,
16 | BluetoothState.POWERED_ON}
17 | )
18 | @Retention(RetentionPolicy.SOURCE)
19 | @interface BluetoothState {
20 |
21 | String UNKNOWN = "Unknown";
22 | String RESETTING = "Resetting";
23 | String UNSUPPORTED = "Unsupported";
24 | String UNAUTHORIZED = "Unauthorized";
25 | String POWERED_OFF = "PoweredOff";
26 | String POWERED_ON = "PoweredOn";
27 | }
28 |
29 | @StringDef({
30 | BluetoothLogLevel.NONE,
31 | BluetoothLogLevel.VERBOSE,
32 | BluetoothLogLevel.DEBUG,
33 | BluetoothLogLevel.INFO,
34 | BluetoothLogLevel.WARNING,
35 | BluetoothLogLevel.ERROR}
36 | )
37 | @Retention(RetentionPolicy.SOURCE)
38 | @interface BluetoothLogLevel {
39 |
40 | String NONE = "None";
41 | String VERBOSE = "Verbose";
42 | String DEBUG = "Debug";
43 | String INFO = "Info";
44 | String WARNING = "Warning";
45 | String ERROR = "Error";
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/ios/ReactBLEScanner/AppDelegate.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2015-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import "AppDelegate.h"
11 |
12 | #import "RCTBundleURLProvider.h"
13 | #import "RCTRootView.h"
14 |
15 | @implementation AppDelegate
16 |
17 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
18 | {
19 | NSURL *jsCodeLocation;
20 |
21 | jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];
22 |
23 | RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
24 | moduleName:@"ReactBLEScanner"
25 | initialProperties:nil
26 | launchOptions:launchOptions];
27 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
28 |
29 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
30 | UIViewController *rootViewController = [UIViewController new];
31 | rootViewController.view = rootView;
32 | self.window.rootViewController = rootViewController;
33 | [self.window makeKeyAndVisible];
34 | return YES;
35 | }
36 |
37 | @end
38 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-ble-plx",
3 | "version": "0.6.0",
4 | "description": "React Native Bluetooth Low Energy library",
5 | "main": "index.js",
6 | "directories": {
7 | "example": "examples"
8 | },
9 | "scripts": {
10 | "postinstall": "bash build_ios_frameworks.sh",
11 | "test": "jest",
12 | "lint": "flow && documentation lint index.js",
13 | "docs": "documentation build index.js -o docs --config documentation.yml -f html"
14 | },
15 | "repository": {
16 | "type": "git",
17 | "url": "git+https://github.com/Polidea/react-native-ble-plx.git"
18 | },
19 | "devDependencies": {
20 | "babel-eslint": "^7.2.3",
21 | "babel-jest": "^20.0.3",
22 | "babel-preset-react-native": "^1.9.2",
23 | "documentation": "4.0.0-rc.1",
24 | "eslint": "^3.19.0",
25 | "eslint-plugin-flowtype": "^2.33.0",
26 | "eslint-plugin-import": "^2.2.0",
27 | "eslint-plugin-jest": "^20.0.3",
28 | "eslint-plugin-react": "^7.0.1",
29 | "flow-bin": "^0.42.0",
30 | "jest": "^20.0.3",
31 | "prettier": "^1.3.1",
32 | "react": "16.0.0-alpha.6",
33 | "react-native": "0.44.0"
34 | },
35 | "keywords": [
36 | "React",
37 | "Native",
38 | "Bluetooth",
39 | "Low",
40 | "Energy",
41 | "BLE",
42 | "Polidea"
43 | ],
44 | "jest": {
45 | "preset": "react-native"
46 | },
47 | "author": "Polidea",
48 | "license": "Apache-2.0",
49 | "bugs": {
50 | "url": "https://github.com/Polidea/react-native-ble-plx/issues"
51 | },
52 | "homepage": "https://github.com/Polidea/react-native-ble-plx#readme"
53 | }
--------------------------------------------------------------------------------
/__tests__/Service.js:
--------------------------------------------------------------------------------
1 | jest.mock('../src/BleManager')
2 | import { BleManager } from '../src/BleManager'
3 | import { Service } from '../src/Service'
4 |
5 | describe("Test if Service is properly calling BleManager's utility function: ", () => {
6 | const bleManager = new BleManager()
7 | const service = new Service({ id: 'serviceId', uuid: 'serviceUUID', deviceID: 'deviceId' }, bleManager)
8 |
9 | test('characteristics', async () => {
10 | await service.characteristics()
11 | expect(bleManager._characteristicsForService).toBeCalledWith('serviceId')
12 | })
13 |
14 | test('readCharacteristic', async () => {
15 | await service.readCharacteristic('bbbb', 'id')
16 | expect(bleManager._readCharacteristicForService).toBeCalledWith('serviceId', 'bbbb', 'id')
17 | })
18 |
19 | test('writeCharacteristicWithResponse', async () => {
20 | await service.writeCharacteristicWithResponse('bbbb', 'value', 'id')
21 | expect(bleManager._writeCharacteristicWithResponseForService).toBeCalledWith('serviceId', 'bbbb', 'value', 'id')
22 | })
23 |
24 | test('writeCharacteristicWithoutResponse', async () => {
25 | await service.writeCharacteristicWithoutResponse('bbbb', 'value', 'id')
26 | expect(bleManager._writeCharacteristicWithoutResponseForService).toBeCalledWith('serviceId', 'bbbb', 'value', 'id')
27 | })
28 |
29 | test('monitorCharacteristic', async () => {
30 | const listener = jest.fn()
31 | await service.monitorCharacteristic('bbbb', listener, 'id')
32 | expect(bleManager._monitorCharacteristicForService).toBeCalledWith('serviceId', 'bbbb', listener, 'id')
33 | })
34 | })
35 |
--------------------------------------------------------------------------------
/.flowconfig:
--------------------------------------------------------------------------------
1 | [ignore]
2 | ; We fork some components by platform
3 | .*/*[.]android.js
4 |
5 | ; Ignore "BUCK" generated dirs
6 | /\.buckd/
7 |
8 | ; Ignore unexpected extra "@providesModule"
9 | .*/node_modules/.*/node_modules/fbjs/.*
10 | .*/node_modules/documentation
11 |
12 | ; Ignore duplicate module providers
13 | ; For RN Apps installed via npm, "Libraries" folder is inside
14 | ; "node_modules/react-native" but in the source repo it is in the root
15 | .*/Libraries/react-native/React.js
16 | .*/Libraries/react-native/ReactNative.js
17 |
18 | [include]
19 |
20 | [libs]
21 | node_modules/react-native/Libraries/react-native/react-native-interface.js
22 | node_modules/react-native/flow
23 | flow/
24 |
25 | [options]
26 | emoji=true
27 |
28 | module.system=haste
29 |
30 | experimental.strict_type_args=true
31 |
32 | munge_underscores=true
33 |
34 | module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
35 |
36 | suppress_type=$FlowIssue
37 | suppress_type=$FlowFixMe
38 | suppress_type=$FixMe
39 |
40 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(4[0-2]\\|[1-3][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
41 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(4[0-2]\\|[1-3][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
42 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
43 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
44 |
45 | unsafe.enable_getters_and_setters=true
46 |
47 | [version]
48 | ^0.42.0
49 |
--------------------------------------------------------------------------------
/ios/BleClientManager/BleUtils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BleUtils.swift
3 | //
4 | // Created by Przemysław Lenart on 20/07/16.
5 | //
6 |
7 | import Foundation
8 | import RxSwift
9 | import CoreBluetooth
10 |
11 | extension Sequence where Iterator.Element == String {
12 | func toCBUUIDS() -> [CBUUID]? {
13 | var newUUIDS: [CBUUID] = []
14 | for uuid in self {
15 | guard let nsuuid = uuid.toCBUUID() else {
16 | return nil;
17 | }
18 | newUUIDS.append(nsuuid)
19 | }
20 | return newUUIDS
21 | }
22 | }
23 |
24 | extension String {
25 | func toCBUUID() -> CBUUID? {
26 | let uuid: String
27 | switch self.characters.count {
28 | case 4:
29 | uuid = "0000\(self)-0000-1000-8000-00805f9b34fb"
30 | case 8:
31 | uuid = "\(self)-0000-1000-8000-00805f9b34fb"
32 | default:
33 | uuid = self
34 | }
35 | guard let nsuuid = UUID(uuidString: uuid) else {
36 | return nil
37 | }
38 | return CBUUID(nsuuid: nsuuid)
39 | }
40 | }
41 |
42 | extension CBUUID {
43 | var fullUUIDString: String {
44 | let native = self.uuidString.lowercased()
45 | if (native.characters.count == 4) {
46 | return "0000\(native)-0000-1000-8000-00805f9b34fb"
47 | }
48 | if (native.characters.count == 8) {
49 | return "\(native)-0000-1000-8000-00805f9b34fb"
50 | }
51 | return native
52 | }
53 | }
54 |
55 | extension Data {
56 | var base64: String {
57 | return self.base64EncodedString(options: .endLineWithCarriageReturn)
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/reactnativeble/utils/SafePromise.java:
--------------------------------------------------------------------------------
1 | package com.polidea.reactnativeble.utils;
2 |
3 | import com.facebook.react.bridge.Promise;
4 |
5 | import java.util.concurrent.atomic.AtomicBoolean;
6 |
7 | import javax.annotation.Nullable;
8 |
9 | public class SafePromise implements Promise {
10 | private Promise promise;
11 | private AtomicBoolean isFinished = new AtomicBoolean();
12 |
13 | public SafePromise(Promise promise) {
14 | this.promise = promise;
15 | }
16 |
17 | @Override
18 | public void resolve(@Nullable Object value) {
19 | if (isFinished.compareAndSet(false, true)) {
20 | promise.resolve(value);
21 | }
22 | }
23 |
24 | @Override
25 | public void reject(String code, String message) {
26 | if (isFinished.compareAndSet(false, true)) {
27 | promise.reject(code, message);
28 | }
29 | }
30 |
31 | @Override
32 | public void reject(String code, Throwable e) {
33 | if (isFinished.compareAndSet(false, true)) {
34 | promise.reject(code, e);
35 | }
36 | }
37 |
38 | @Override
39 | public void reject(String code, String message, Throwable e) {
40 | if (isFinished.compareAndSet(false, true)) {
41 | promise.reject(code, message, e);
42 | }
43 | }
44 |
45 | @Deprecated
46 | @Override
47 | public void reject(String message) {
48 | if (isFinished.compareAndSet(false, true)) {
49 | promise.reject(message);
50 | }
51 | }
52 |
53 | @Override
54 | public void reject(Throwable reason) {
55 | if (isFinished.compareAndSet(false, true)) {
56 | promise.reject(reason);
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/app/services/ServicesComponent.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, { Component } from 'react';
4 | import { View, Text } from 'react-native';
5 | import { connect } from 'react-redux'
6 | import * as ble from '../ble/BleActions'
7 | import ServiceView from './ServiceView'
8 | import ImmutableListView from '../view/ImmutableListView'
9 | import * as SceneConst from '../scene/Const'
10 | import { Actions } from 'react-native-router-flux'
11 | import Style from '../view/Style'
12 |
13 | class ServicesComponent extends Component {
14 |
15 | _renderServiceCell(rowData) {
16 | const serviceClicked = () => {
17 | this.props.selectService(this.props.deviceId, rowData.get('uuid'))
18 | Actions[SceneConst.CHARACTERISTICS_SCENE]();
19 | }
20 |
21 | return (
22 |
28 | )
29 | }
30 |
31 | render() {
32 | return (
33 |
34 |
38 | Device status: {this.props.state}
39 |
40 | )
41 | }
42 | }
43 |
44 | export default connect(
45 | state => {
46 | const deviceId = state.getIn(['ble', 'selectedDeviceId'])
47 | return {
48 | deviceId,
49 | services: state.getIn(['ble', 'devices', deviceId, 'services']),
50 | state: state.getIn(['ble', 'state'])
51 | }
52 | },
53 | {
54 | selectService: ble.selectService
55 | })
56 | (ServicesComponent)
57 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/ios/ReactBLEScanner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 2
23 | LSRequiresIPhoneOS
24 |
25 | NSAppTransportSecurity
26 |
27 | NSExceptionDomains
28 |
29 | localhost
30 |
31 | NSTemporaryExceptionAllowsInsecureHTTPLoads
32 |
33 |
34 |
35 |
36 | NSLocationWhenInUseUsageDescription
37 |
38 | UILaunchStoryboardName
39 | LaunchScreen
40 | UIRequiredDeviceCapabilities
41 |
42 | armv7
43 |
44 | UISupportedInterfaceOrientations
45 |
46 | UIInterfaceOrientationPortrait
47 | UIInterfaceOrientationLandscapeLeft
48 | UIInterfaceOrientationLandscapeRight
49 |
50 | UIViewControllerBasedStatusBarAppearance
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/reactnativeble/utils/LogLevel.java:
--------------------------------------------------------------------------------
1 | package com.polidea.reactnativeble.utils;
2 |
3 | import com.polidea.rxandroidble.internal.RxBleLog;
4 |
5 | public class LogLevel {
6 |
7 | @RxBleLog.LogLevel
8 | public static int toLogLevel(String logLevel) {
9 | switch (logLevel) {
10 | case Constants.BluetoothLogLevel.VERBOSE:
11 | return RxBleLog.VERBOSE;
12 | case Constants.BluetoothLogLevel.DEBUG:
13 | return RxBleLog.DEBUG;
14 | case Constants.BluetoothLogLevel.INFO:
15 | return RxBleLog.INFO;
16 | case Constants.BluetoothLogLevel.WARNING:
17 | return RxBleLog.WARN;
18 | case Constants.BluetoothLogLevel.ERROR:
19 | return RxBleLog.ERROR;
20 | case Constants.BluetoothLogLevel.NONE:
21 | // fallthrough
22 | default:
23 | return RxBleLog.NONE;
24 | }
25 | }
26 |
27 | @Constants.BluetoothLogLevel
28 | public static String fromLogLevel(int logLevel) {
29 | switch (logLevel) {
30 | case RxBleLog.VERBOSE:
31 | return Constants.BluetoothLogLevel.VERBOSE;
32 | case RxBleLog.DEBUG:
33 | return Constants.BluetoothLogLevel.DEBUG;
34 | case RxBleLog.INFO:
35 | return Constants.BluetoothLogLevel.INFO;
36 | case RxBleLog.WARN:
37 | return Constants.BluetoothLogLevel.WARNING;
38 | case RxBleLog.ERROR:
39 | return Constants.BluetoothLogLevel.ERROR;
40 | case RxBleLog.NONE:
41 | // fallthrough
42 | default:
43 | return Constants.BluetoothLogLevel.NONE;
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/android/app/BUCK:
--------------------------------------------------------------------------------
1 | import re
2 |
3 | # To learn about Buck see [Docs](https://buckbuild.com/).
4 | # To run your application with Buck:
5 | # - install Buck
6 | # - `npm start` - to start the packager
7 | # - `cd android`
8 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
9 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
10 | # - `buck install -r android/app` - compile, install and run application
11 | #
12 |
13 | lib_deps = []
14 | for jarfile in glob(['libs/*.jar']):
15 | name = 'jars__' + re.sub(r'^.*/([^/]+)\.jar$', r'\1', jarfile)
16 | lib_deps.append(':' + name)
17 | prebuilt_jar(
18 | name = name,
19 | binary_jar = jarfile,
20 | )
21 |
22 | for aarfile in glob(['libs/*.aar']):
23 | name = 'aars__' + re.sub(r'^.*/([^/]+)\.aar$', r'\1', aarfile)
24 | lib_deps.append(':' + name)
25 | android_prebuilt_aar(
26 | name = name,
27 | aar = aarfile,
28 | )
29 |
30 | android_library(
31 | name = 'all-libs',
32 | exported_deps = lib_deps
33 | )
34 |
35 | android_library(
36 | name = 'app-code',
37 | srcs = glob([
38 | 'src/main/java/**/*.java',
39 | ]),
40 | deps = [
41 | ':all-libs',
42 | ':build_config',
43 | ':res',
44 | ],
45 | )
46 |
47 | android_build_config(
48 | name = 'build_config',
49 | package = 'com.polidea.sniffator',
50 | )
51 |
52 | android_resource(
53 | name = 'res',
54 | res = 'src/main/res',
55 | package = 'com.polidea.sniffator',
56 | )
57 |
58 | android_binary(
59 | name = 'app',
60 | package_type = 'debug',
61 | manifest = 'src/main/AndroidManifest.xml',
62 | keystore = '//android/keystores:debug',
63 | deps = [
64 | ':app-code',
65 | ],
66 | )
67 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/app/root/RootComponent.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, { Component } from 'react';
4 | import { Router, Scene } from 'react-native-router-flux';
5 | import { connect } from 'react-redux';
6 | import ScannedDevicesComponent from '../scanning/ScannedDevicesComponent';
7 | import ServicesComponent from '../services/ServicesComponent';
8 | import CharacteristicsComponent from '../characteristics/CharacteristicsComponent';
9 | import CharacteristicDetailsComponent from '../characteristics/CharacteristicDetailsComponent';
10 | import * as SceneConst from '../scene/Const.js'
11 | import * as ble from '../ble/BleActions'
12 | import { Actions } from 'react-native-router-flux'
13 |
14 | const RouterWithRedux = connect()(Router)
15 |
16 | class RootComponent extends Component {
17 |
18 | _onBack() {
19 | Actions.pop();
20 | this.props.changeDeviceState(this.props.selectedDeviceId, ble.DEVICE_STATE_DISCONNECT);
21 | }
22 |
23 | render() {
24 | return (
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | )
34 | }
35 | }
36 |
37 | export default connect((state) => ({
38 | selectedDeviceId: state.getIn(['ble', 'selectedDeviceId']),
39 | sceneState: state.getIn(['route', 'state'])
40 | }), {
41 | changeDeviceState: ble.changeDeviceState
42 | })(RootComponent)
43 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/index.android.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, { Component } from 'react';
4 | import { View, AppRegistry } from 'react-native';
5 | import { applyMiddleware, createStore } from 'redux';
6 | import { Provider } from 'react-redux';
7 | import createLogger from 'redux-logger';
8 |
9 | import RootComponent from './app/root/RootComponent';
10 | import ErrorComponent from './app/root/ErrorComponent';
11 | import BleComponent from './app/ble/BleComponent';
12 | import reducer from './app/root/Reducer';
13 |
14 | import { Iterable } from 'immutable';
15 |
16 | import { checkPermission } from 'react-native-android-permissions';
17 | import { requestPermission } from 'react-native-android-permissions';
18 |
19 | const stateTransformer = (state) => {
20 | if (Iterable.isIterable(state)) {
21 | return state.toJS()
22 | } else {
23 | return state;
24 | }
25 | };
26 |
27 | const logger = createLogger({ stateTransformer });
28 | const store = createStore(reducer)
29 |
30 | class ReactBLEScanner extends Component {
31 |
32 | componentDidMount() {
33 | this.checkAndGrantPermissions()
34 | }
35 |
36 | checkAndGrantPermissions() {
37 | checkPermission("android.permission.ACCESS_COARSE_LOCATION").then((result) => {
38 | console.log("Already Granted!");
39 | console.log(result);
40 | }, (result) => {
41 | console.log("Not Granted!");
42 | console.log(result);
43 | requestPermission("android.permission.ACCESS_COARSE_LOCATION").then((result) => {
44 | console.log("Granted!", result);
45 | }, (result) => {
46 | console.log("Not Granted!");
47 | console.log(result);
48 | });
49 | });
50 | }
51 |
52 | render() {
53 | return (
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | );
62 | }
63 | }
64 |
65 | AppRegistry.registerComponent('ReactBLEScanner', () => ReactBLEScanner);
66 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/app/characteristics/CharacteristicsComponent.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, { Component } from 'react';
4 | import { View, Text } from 'react-native';
5 | import { connect } from 'react-redux'
6 | import CharacteristicView from './CharacteristicView'
7 | import ImmutableListView from '../view/ImmutableListView'
8 | import Style from '../view/Style'
9 | import { Actions } from 'react-native-router-flux'
10 | import * as SceneConst from '../scene/Const'
11 | import * as ble from '../ble/BleActions'
12 |
13 | class CharacteristicsComponent extends Component {
14 |
15 | _characteristicClicked(rowData) {
16 | this.props.selectCharacteristic(this.props.deviceId, this.props.serviceId, rowData.get('uuid'))
17 | Actions[SceneConst.CHARACTERISTIC_DETAILS_SCENE]();
18 | }
19 |
20 | _renderCharacteristicCell(rowData) {
21 | return (
22 |
30 | )
31 | }
32 |
33 | render() {
34 | return (
35 |
36 |
39 | Device status: {this.props.state}
40 |
41 | )
42 | }
43 | }
44 |
45 | export default connect(
46 | state => {
47 | const deviceId = state.getIn(['ble', 'selectedDeviceId']);
48 | const serviceId = state.getIn(['ble', 'selectedServiceId']);
49 |
50 | return {
51 | deviceId,
52 | serviceId,
53 | state: state.getIn(['ble', 'state']),
54 | characteristics: state.getIn(['ble', 'devices', deviceId, 'services', serviceId, 'characteristics'])
55 | }
56 | },
57 | {
58 | selectCharacteristic: ble.selectCharacteristic
59 | })
60 | (CharacteristicsComponent)
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/reactnativeble/errors/BleError.java:
--------------------------------------------------------------------------------
1 | package com.polidea.reactnativeble.errors;
2 |
3 | import android.support.annotation.NonNull;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | public class BleError {
9 | public static Error unknown() {
10 | return new Error("Unknown error", 0);
11 | }
12 |
13 | public static Error cancelled() {
14 | return new Error("Cancelled", 1);
15 | }
16 |
17 | static public Error invalidUUIDs(@NonNull String... uuids) {
18 | String uuidsString = "";
19 | for (String uuid: uuids) {
20 | uuidsString += uuid + ", ";
21 | }
22 | return new Error("Invalid UUIDs were passed: " + uuidsString, 500);
23 | }
24 |
25 | static public Error deviceNotFound(String uuid) {
26 | return new Error("Device " + uuid + " not found", 501);
27 | }
28 |
29 | static public Error deviceNotConnected(String uuid) {
30 | return new Error("Device " + uuid + " not connected", 502);
31 | }
32 |
33 | static public Error characteristicNotFound(String uuid) {
34 | return new Error("Characteristic with uuid " + uuid + " not found", 503);
35 | }
36 |
37 | static public Error characteristicNotFound(int id) {
38 | return new Error("Characteristic with id " + id + " not found", 503);
39 | }
40 |
41 | static public Error serviceNotFound(String uuid) {
42 | return new Error("Service with uuid " + uuid + " not found", 504);
43 | }
44 |
45 | static public Error serviceNotFound(int id) {
46 | return new Error("Service with id " + id + " not found", 504);
47 | }
48 |
49 | static public Error invalidWriteDataForCharacteristic(String data, String uuid) {
50 | return new Error("Invalid base64 write data: " + data + " for characteristic " + uuid, 505);
51 | }
52 |
53 | static public Error cannotMonitorCharacteristic(String uuid) {
54 | return new Error("Characteristic " + uuid + " cannot be monitored as it doesn't support notifications or indications", 506);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/__tests__/Utils.js:
--------------------------------------------------------------------------------
1 | const EventEmitter = require('events')
2 | import { fullUUID } from '../src/Utils'
3 |
4 | export class NativeEventEmitter extends EventEmitter {
5 | constructor(module) {
6 | super()
7 | module.emit = this.emit.bind(this)
8 | }
9 | emit() {
10 | return super.emit.apply(this, arguments)
11 | }
12 | addListener(eventName, listener) {
13 | super.addListener(eventName, listener)
14 | return {
15 | remove: () => {
16 | super.removeListener(eventName, listener)
17 | }
18 | }
19 | }
20 | }
21 |
22 | test('Mocked NativeEventEmitter allows to emit events directly by module', () => {
23 | const module = {
24 | emit: jest.fn()
25 | }
26 | const emitter = new NativeEventEmitter(module)
27 |
28 | const listener = jest.fn()
29 | const listener2 = jest.fn()
30 |
31 | emitter.addListener('event', listener)
32 | emitter.addListener('event2', listener2)
33 |
34 | module.emit('event', 'a')
35 | module.emit('event2', 'b')
36 | module.emit('event2', 'c')
37 |
38 | expect(listener.mock.calls).toEqual([['a']])
39 | expect(listener2.mock.calls).toEqual([['b'], ['c']])
40 | })
41 |
42 | test('Mocked NativeEventEmitter allows to unsubscribe from events', () => {
43 | const module = {
44 | emit: jest.fn()
45 | }
46 | const emitter = new NativeEventEmitter(module)
47 | const listener = jest.fn()
48 |
49 | module.emit('event', 'a')
50 | const subscription = emitter.addListener('event', listener)
51 | module.emit('event', 'b')
52 | subscription.remove()
53 | module.emit('event', 'c')
54 | expect(listener.mock.calls).toEqual([['b']])
55 | })
56 |
57 | test('fullUUID properly transforms 16bit UUID', () => {
58 | expect(fullUUID('180A')).toBe('0000180a-0000-1000-8000-00805f9b34fb')
59 | })
60 |
61 | test('fullUUID properly transforms 32bit UUID', () => {
62 | expect(fullUUID('180AffFF')).toBe('180affff-0000-1000-8000-00805f9b34fb')
63 | })
64 |
65 | test('fullUUID properly transforms 128bit UUID', () => {
66 | expect(fullUUID('0000180A-0000-1000-8000-00805f9B34Fb')).toBe('0000180a-0000-1000-8000-00805f9b34fb')
67 | })
68 |
--------------------------------------------------------------------------------
/android/src/test/groovy/com/polidea/reactnativeble/utils/UUIDConverterSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.polidea.reactnativeble.utils
2 |
3 | import spock.lang.Specification
4 |
5 | class UUIDConverterSpec extends Specification {
6 | def "UUID strings should be converted properly to UUID object"(String shortUUID, String uuid) {
7 | when:
8 | def upperCaseUUID = UUIDConverter.convert(shortUUID.toUpperCase())
9 | def lowerCaseUUID = UUIDConverter.convert(shortUUID.toLowerCase())
10 | def mixedCaseUUID = UUIDConverter.convert(shortUUID)
11 | def baseUUID = UUID.fromString(uuid)
12 |
13 | then:
14 | baseUUID == upperCaseUUID
15 | baseUUID == lowerCaseUUID
16 | baseUUID == mixedCaseUUID
17 |
18 | where:
19 | shortUUID | uuid
20 | "180A" | "0000180a-0000-1000-8000-00805f9b34fb"
21 | "280b180A" | "280b180a-0000-1000-8000-00805f9b34fb"
22 | "55d2EA0c-99c3-11e6-9F33-a24fc0d9649c" | "55d2ea0c-99c3-11e6-9f33-a24fc0d9649c"
23 | }
24 |
25 | def "UUID array of strings should be converted properly to array of UUID objects"() {
26 | when:
27 | def uuids = UUIDConverter.convert("180A", "280b180A", "55d2EA0c-99c3-11e6-9F33-a24fc0d9649c")
28 | UUID[] refUUIDs = [UUID.fromString("0000180a-0000-1000-8000-00805f9b34fb"),
29 | UUID.fromString("280b180a-0000-1000-8000-00805f9b34fb"),
30 | UUID.fromString("55d2ea0c-99c3-11e6-9f33-a24fc0d9649c")]
31 |
32 | then:
33 | Arrays.equals(uuids, refUUIDs)
34 | }
35 |
36 | def "fromUUID always returns full lowercase UUID"(String uuid) {
37 | when:
38 | def refUUID = UUIDConverter.fromUUID(UUID.fromString(uuid))
39 |
40 | then:
41 | uuid == refUUID
42 |
43 | where:
44 | uuid | _
45 | "0000180a-0000-1000-8000-00805f9b34fb" | _
46 | "280b180a-0000-1000-8000-00805f9b34fb" | _
47 | "55d2ea0c-99c3-11e6-9f33-a24fc0d9649c" | _
48 | }
49 | }
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/app/scanning/ScannedDeviceView.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, { Component, PropTypes } from 'react';
4 | import { View, TouchableOpacity, Text, StyleSheet } from 'react-native';
5 |
6 | let borderColor = '#2d7599'
7 |
8 | const ScannedDeviceView = ({name, uuid, rssi, onClick}) => {
9 | return (
10 |
11 |
12 |
13 |
14 | Name:
15 | {name ? name : '-'}
16 |
17 |
18 | RSSI:
19 | {rssi}
20 |
21 |
22 |
23 |
24 | UUID:
25 | {uuid}
26 |
27 |
28 |
29 | )
30 | }
31 |
32 | ScannedDeviceView.propTypes = {
33 | name: PropTypes.string,
34 | uuid: PropTypes.string.isRequired,
35 | rssi: PropTypes.number.isRequired,
36 | onClick: PropTypes.func.isRequired
37 | }
38 |
39 | var styles = StyleSheet.create({
40 | background: {
41 | flexDirection: 'column',
42 | justifyContent: 'center',
43 | borderColor: borderColor,
44 | borderWidth: 2,
45 | overflow: 'hidden'
46 | },
47 | titleText: {
48 | fontWeight: 'bold',
49 | fontSize: 8,
50 | backgroundColor: '#d2f3ff',
51 | padding: 2
52 | },
53 | contentText: {
54 | paddingHorizontal: 2,
55 | paddingVertical: 3
56 | },
57 | uuidText: {
58 | paddingHorizontal: 2,
59 | paddingVertical: 3,
60 | fontSize: 10,
61 | },
62 | topRow: {
63 | flex: 1,
64 | flexDirection: 'row',
65 | backgroundColor: 'white',
66 | },
67 | bottomRow: {
68 | flex: 1,
69 | backgroundColor: 'white',
70 | },
71 | nameRow: {
72 | flex: 1,
73 | },
74 | rssiRow: {
75 | flex: 1,
76 | }
77 | });
78 |
79 | export default ScannedDeviceView
80 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/app/services/ServiceView.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, { Component, PropTypes } from 'react';
4 | import { View, TouchableOpacity, Text, StyleSheet } from 'react-native';
5 |
6 | let borderColor = '#a2001d'
7 |
8 | const ServiceView = ({characteristicsCount, isPrimary, uuid, onClick}) => {
9 | return (
10 |
11 |
12 |
13 |
14 | Characteristic #:
15 | {characteristicsCount ? characteristicsCount : '-'}
16 |
17 |
18 | isPrimary:
19 | {isPrimary ? 'true' : 'false'}
20 |
21 |
22 |
23 |
24 | UUID:
25 | {uuid}
26 |
27 |
28 |
29 | )
30 | }
31 |
32 | ServiceView.propTypes = {
33 | characteristicsCount: PropTypes.number,
34 | isPrimary: PropTypes.bool.isRequired,
35 | uuid: PropTypes.string.isRequired,
36 | onClick: PropTypes.func
37 | }
38 |
39 | var styles = StyleSheet.create({
40 | background: {
41 | flexDirection: 'column',
42 | justifyContent: 'center',
43 | borderColor: borderColor,
44 | borderWidth: 2,
45 | overflow: 'hidden'
46 | },
47 | titleText: {
48 | fontWeight: 'bold',
49 | fontSize: 8,
50 | backgroundColor: '#ffd0d3',
51 | padding: 2
52 | },
53 | contentText: {
54 | paddingHorizontal: 2,
55 | paddingVertical: 3
56 | },
57 | uuidText: {
58 | paddingHorizontal: 2,
59 | paddingVertical: 3,
60 | fontSize: 10,
61 | },
62 | topRow: {
63 | flex: 1,
64 | flexDirection: 'row',
65 | backgroundColor: 'white',
66 | },
67 | bottomRow: {
68 | flex: 1,
69 | backgroundColor: 'white',
70 | },
71 | row: {
72 | flex: 1,
73 | }
74 | });
75 |
76 | export default ServiceView
77 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/reactnativeble/utils/UUIDConverter.java:
--------------------------------------------------------------------------------
1 | package com.polidea.reactnativeble.utils;
2 |
3 | import com.facebook.react.bridge.ReadableArray;
4 |
5 | import java.util.UUID;
6 |
7 | public class UUIDConverter {
8 |
9 | private static String baseUUIDPrefix = "0000";
10 | private static String baseUUIDSuffix = "-0000-1000-8000-00805F9B34FB";
11 |
12 | public static UUID convert(String sUUID) {
13 | if (sUUID.length() == 4) {
14 | sUUID = baseUUIDPrefix + sUUID + baseUUIDSuffix;
15 | }
16 | else if (sUUID.length() == 8) {
17 | sUUID = sUUID + baseUUIDSuffix;
18 | }
19 | try {
20 | return UUID.fromString(sUUID);
21 | } catch (Throwable e) {
22 | return null;
23 | }
24 | }
25 |
26 | public static UUID[] convert(String... sUUIDs) {
27 | UUID[] UUIDs = new UUID[sUUIDs.length];
28 | for (int i = 0; i < sUUIDs.length; i++) {
29 | try {
30 | if (sUUIDs[i].length() == 4) {
31 | sUUIDs[i] = baseUUIDPrefix + sUUIDs[i] + baseUUIDSuffix;
32 | }
33 | else if (sUUIDs[i].length() == 8) {
34 | sUUIDs[i] = sUUIDs[i] + baseUUIDSuffix;
35 | }
36 | UUIDs[i] = UUID.fromString(sUUIDs[i]);
37 | } catch (Throwable e) {
38 | return null;
39 | }
40 | }
41 | return UUIDs;
42 | }
43 |
44 | public static UUID[] convert(ReadableArray aUUIDs) {
45 | UUID[] UUIDs = new UUID[aUUIDs.size()];
46 | for (int i = 0; i < aUUIDs.size(); i++) {
47 | try {
48 | String sUUID = aUUIDs.getString(i);
49 | if (sUUID.length() == 4) {
50 | sUUID = baseUUIDPrefix + sUUID + baseUUIDSuffix;
51 | } else if (sUUID.length() == 8) {
52 | sUUID = sUUID + baseUUIDSuffix;
53 | }
54 | UUIDs[i] = UUID.fromString(sUUID);
55 | } catch (Throwable e) {
56 | return null;
57 | }
58 | }
59 | return UUIDs;
60 | }
61 |
62 | public static String fromUUID(UUID uuid) {
63 | return uuid.toString().toLowerCase();
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/docs/assets/github.css:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | github.com style (c) Vasily Polovnyov
4 |
5 | */
6 |
7 | .hljs {
8 | display: block;
9 | overflow-x: auto;
10 | padding: 0.5em;
11 | color: #333;
12 | background: #f8f8f8;
13 | -webkit-text-size-adjust: none;
14 | }
15 |
16 | .hljs-comment,
17 | .diff .hljs-header,
18 | .hljs-javadoc {
19 | color: #998;
20 | font-style: italic;
21 | }
22 |
23 | .hljs-keyword,
24 | .css .rule .hljs-keyword,
25 | .hljs-winutils,
26 | .nginx .hljs-title,
27 | .hljs-subst,
28 | .hljs-request,
29 | .hljs-status {
30 | color: #1184CE;
31 | }
32 |
33 | .hljs-number,
34 | .hljs-hexcolor,
35 | .ruby .hljs-constant {
36 | color: #ed225d;
37 | }
38 |
39 | .hljs-string,
40 | .hljs-tag .hljs-value,
41 | .hljs-phpdoc,
42 | .hljs-dartdoc,
43 | .tex .hljs-formula {
44 | color: #ed225d;
45 | }
46 |
47 | .hljs-title,
48 | .hljs-id,
49 | .scss .hljs-preprocessor {
50 | color: #900;
51 | font-weight: bold;
52 | }
53 |
54 | .hljs-list .hljs-keyword,
55 | .hljs-subst {
56 | font-weight: normal;
57 | }
58 |
59 | .hljs-class .hljs-title,
60 | .hljs-type,
61 | .vhdl .hljs-literal,
62 | .tex .hljs-command {
63 | color: #458;
64 | font-weight: bold;
65 | }
66 |
67 | .hljs-tag,
68 | .hljs-tag .hljs-title,
69 | .hljs-rules .hljs-property,
70 | .django .hljs-tag .hljs-keyword {
71 | color: #000080;
72 | font-weight: normal;
73 | }
74 |
75 | .hljs-attribute,
76 | .hljs-variable,
77 | .lisp .hljs-body {
78 | color: #008080;
79 | }
80 |
81 | .hljs-regexp {
82 | color: #009926;
83 | }
84 |
85 | .hljs-symbol,
86 | .ruby .hljs-symbol .hljs-string,
87 | .lisp .hljs-keyword,
88 | .clojure .hljs-keyword,
89 | .scheme .hljs-keyword,
90 | .tex .hljs-special,
91 | .hljs-prompt {
92 | color: #990073;
93 | }
94 |
95 | .hljs-built_in {
96 | color: #0086b3;
97 | }
98 |
99 | .hljs-preprocessor,
100 | .hljs-pragma,
101 | .hljs-pi,
102 | .hljs-doctype,
103 | .hljs-shebang,
104 | .hljs-cdata {
105 | color: #999;
106 | font-weight: bold;
107 | }
108 |
109 | .hljs-deletion {
110 | background: #fdd;
111 | }
112 |
113 | .hljs-addition {
114 | background: #dfd;
115 | }
116 |
117 | .diff .hljs-change {
118 | background: #0086b3;
119 | }
120 |
121 | .hljs-chunk {
122 | color: #aaa;
123 | }
124 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | **0.6.0**
2 | - Added basic API to support background mode. When BleManager is constructed you can pass
3 | `restoreStateIdentifier` and `restoreStateFunction` to `BleManagerOptions` object to
4 | enable support for background mode. More info about usage can be found in documentation.
5 | - All subscriptions and promises are properly "Destroyed" when `destory()` function is called.
6 | - Fixed bug on Android where notification messages could be duplicated or skipped.
7 | - Updated RxAndroidBle to version 1.3
8 | - Updated README file.
9 | - Updated library logo
10 |
11 | **0.5.0**
12 | - Added new API for supporting unique Services and Characteristics:
13 | * `Characteristic.id`, `Service.id` fields which uniquely identify BLE objects.
14 | * All utility functions which don't require UUIDs as arguments are using
15 | internally `id` fields and therefore work faster and properly handle
16 | services/characteristics with same UUIDs. For example: `characteristic.read()`.
17 | - New option to enable native modules' logging system via `bleManager.setLogLevel()` function.
18 | - New function to read RSSI for connected devices: `bleManager.readRSSIForDevice()`.
19 | - Updated RxBluetoothKit dependency to version 3.0.12
20 | - Updated RxAndroidBle dependency to 1.2.2
21 | - Added tests for JS API.
22 | - Better Flow type checking and coverage.
23 | - Documentation was moved to `./docs` folder and now is generated by documentation.js.
24 | - Small fixes in examples.
25 | - Updated installation steps.
26 |
27 | **0.4.0**
28 | - Device ID properties were renamed as they are not UUIDs on Android **(breaking change)**:
29 | * `Device.uuid` -> `Device.id`
30 | * `Service.deviceUUID` -> `Service.deviceID`,
31 | * `Characteristic.deviceUUID` -> `Characteristic.deviceID`
32 | - Changed signature of `onDeviceDisconnected`, as Device object is always available.
33 | - Updated to Swift 3.0
34 | - Updated to RxAndroidBle 1.1.0 and RxBluetoothKit 3.0.6
35 | - Documentation was moved to `./doc` folder and now is generated by ESDoc.
36 | - Fixed `state()` invalid return type. Implemented `state()` and `onStateChange()` for Android.
37 | - Added optional parameter to `onStateChange()` function.
38 | - Fixed `monitorCharacteristicForDevice()` for Android when characteristic accepts indications only.
39 | - Updated `AndroidManifest.xml` configuration.
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/app/scanning/ScannedDevicesComponent.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, { Component } from 'react';
4 | import { View, StyleSheet } from 'react-native';
5 | import { connect } from 'react-redux'
6 |
7 | import ButtonView from '../view/ButtonView'
8 | import ImmutableListView from '../view/ImmutableListView'
9 | import ScannedDeviceView from './ScannedDeviceView'
10 | import * as ble from '../ble/BleActions'
11 | import { Actions } from 'react-native-router-flux'
12 | import * as SceneConst from '../scene/Const'
13 | import Style from '../view/Style'
14 |
15 | class ScannedDevicesComponent extends Component {
16 |
17 | _renderScannedDeviceCell(rowData) {
18 | const connectToDevice = () => {
19 | this.props.changeDeviceState(rowData.get('uuid'), ble.DEVICE_STATE_CONNECT)
20 | Actions[SceneConst.SERVICES_SCENE]();
21 | }
22 |
23 | return (
24 |
30 | )
31 | }
32 |
33 | render() {
34 | return (
35 |
36 |
39 |
40 |
45 |
50 |
51 |
52 | )
53 | }
54 | }
55 |
56 | var styles = StyleSheet.create({
57 | title: {
58 | fontWeight: 'bold',
59 | textAlign: 'center',
60 | padding: 10
61 | },
62 | buttonRow: {
63 | flexDirection: 'row',
64 | justifyContent: 'space-between',
65 | },
66 | });
67 |
68 | export default connect(
69 | state => ({
70 | devices: state.getIn(['ble', 'devices']),
71 | scanning: state.getIn(['ble', 'scanning'])
72 | }),
73 | {
74 | startScan: ble.startScan,
75 | stopScan: ble.stopScan,
76 | changeDeviceState: ble.changeDeviceState
77 | })
78 | (ScannedDevicesComponent)
79 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/ios/ReactBLEScannerTests/ReactBLEScannerTests.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2015-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 | #import
12 |
13 | #import "RCTLog.h"
14 | #import "RCTRootView.h"
15 |
16 | #define TIMEOUT_SECONDS 600
17 | #define TEXT_TO_LOOK_FOR @"Welcome to React Native!"
18 |
19 | @interface ReactBLEScannerTests : XCTestCase
20 |
21 | @end
22 |
23 | @implementation ReactBLEScannerTests
24 |
25 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test
26 | {
27 | if (test(view)) {
28 | return YES;
29 | }
30 | for (UIView *subview in [view subviews]) {
31 | if ([self findSubviewInView:subview matching:test]) {
32 | return YES;
33 | }
34 | }
35 | return NO;
36 | }
37 |
38 | - (void)testRendersWelcomeScreen
39 | {
40 | UIViewController *vc = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
41 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
42 | BOOL foundElement = NO;
43 |
44 | __block NSString *redboxError = nil;
45 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
46 | if (level >= RCTLogLevelError) {
47 | redboxError = message;
48 | }
49 | });
50 |
51 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
52 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
53 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
54 |
55 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) {
56 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
57 | return YES;
58 | }
59 | return NO;
60 | }];
61 | }
62 |
63 | RCTSetLogFunction(RCTDefaultLogFunction);
64 |
65 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
66 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
67 | }
68 |
69 |
70 | @end
71 |
--------------------------------------------------------------------------------
/docs/assets/style.css:
--------------------------------------------------------------------------------
1 | .documentation {
2 | font-family: Helvetica, sans-serif;
3 | color: #666;
4 | line-height: 1.5;
5 | background: #f5f5f5;
6 | }
7 |
8 | .black {
9 | color: #666;
10 | }
11 |
12 | .bg-white {
13 | background-color: #fff;
14 | }
15 |
16 | h4 {
17 | margin: 20px 0 10px 0;
18 | }
19 |
20 | .documentation h3 {
21 | color: #000;
22 | }
23 |
24 | .border-bottom {
25 | border-color: #ddd;
26 | }
27 |
28 | a {
29 | color: #e8524b;
30 | text-decoration: none;
31 | }
32 |
33 | .documentation a[href]:hover {
34 | text-decoration: underline;
35 | }
36 |
37 | a:hover {
38 | cursor: pointer;
39 | }
40 |
41 | .py1-ul li {
42 | padding: 5px 0;
43 | }
44 |
45 | .max-height-100 {
46 | max-height: 100%;
47 | }
48 |
49 | section:target h3 {
50 | font-weight: 700;
51 | }
52 |
53 | .documentation td,
54 | .documentation th {
55 | padding: .25rem .25rem;
56 | }
57 |
58 | h1:hover .anchorjs-link,
59 | h2:hover .anchorjs-link,
60 | h3:hover .anchorjs-link,
61 | h4:hover .anchorjs-link {
62 | opacity: 1;
63 | }
64 |
65 | .fix-3 {
66 | width: 25%;
67 | max-width: 244px;
68 | }
69 |
70 | .fix-3 {
71 | width: 25%;
72 | max-width: 244px;
73 | }
74 |
75 | @media (min-width: 52em) {
76 | .fix-margin-3 {
77 | margin-left: 25%;
78 | }
79 | }
80 |
81 | .pre,
82 | pre,
83 | code,
84 | .code {
85 | font-family: Source Code Pro, Menlo, Consolas, Liberation Mono, monospace;
86 | font-size: 14px;
87 | }
88 |
89 | .fill-light {
90 | background: #F9F9F9;
91 | }
92 |
93 | .width2 {
94 | width: 1rem;
95 | }
96 |
97 | .input {
98 | font-family: inherit;
99 | display: block;
100 | width: 100%;
101 | height: 2rem;
102 | padding: .5rem;
103 | margin-bottom: 1rem;
104 | border: 1px solid #ccc;
105 | font-size: .875rem;
106 | border-radius: 3px;
107 | box-sizing: border-box;
108 | }
109 |
110 | table {
111 | border-collapse: collapse;
112 | }
113 |
114 | .prose table th,
115 | .prose table td {
116 | text-align: left;
117 | padding: 8px;
118 | border: 1px solid #ddd;
119 | }
120 |
121 | .prose table th:nth-child(1) {
122 | border-right: none;
123 | }
124 |
125 | .prose table th:nth-child(2) {
126 | border-left: none;
127 | }
128 |
129 | .prose table {
130 | border: 1px solid #ddd;
131 | }
132 |
133 | .prose-big {
134 | font-size: 18px;
135 | line-height: 30px;
136 | }
137 |
138 | .quiet {
139 | opacity: 0.7;
140 | }
141 |
142 | .minishadow {
143 | box-shadow: 2px 2px 10px #f3f3f3;
144 | }
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/reactnativeble/wrapper/Service.java:
--------------------------------------------------------------------------------
1 | package com.polidea.reactnativeble.wrapper;
2 |
3 | import android.bluetooth.BluetoothGattCharacteristic;
4 | import android.bluetooth.BluetoothGattService;
5 | import android.util.Pair;
6 |
7 | import com.facebook.react.bridge.Arguments;
8 | import com.facebook.react.bridge.WritableMap;
9 | import com.polidea.reactnativeble.utils.IdGenerator;
10 | import com.polidea.reactnativeble.utils.UUIDConverter;
11 |
12 | import java.util.ArrayList;
13 | import java.util.List;
14 | import java.util.UUID;
15 |
16 | public class Service {
17 |
18 | private interface Metadata {
19 | String ID = "id";
20 | String UUID = "uuid";
21 | String DEVICE_ID = "deviceID";
22 | String IS_PRIMARY = "isPrimary";
23 | }
24 |
25 | private Device device;
26 | private BluetoothGattService service;
27 | private int id;
28 |
29 | public Service(Device device, BluetoothGattService service) {
30 | this.device = device;
31 | this.service = service;
32 | this.id = IdGenerator.getIdForKey(new Pair<>(service.getUuid(), service.getInstanceId()));
33 | }
34 |
35 | public int getId() {
36 | return this.id;
37 | }
38 |
39 | public Device getDevice() {
40 | return device;
41 | }
42 |
43 | public BluetoothGattService getNativeService() {
44 | return service;
45 | }
46 |
47 | public Characteristic getCharacteristicByUUID(UUID uuid) {
48 | BluetoothGattCharacteristic characteristic = service.getCharacteristic(uuid);
49 | if (characteristic == null) return null;
50 | return new Characteristic(this, characteristic);
51 | }
52 |
53 | public List getCharacteristics() {
54 | ArrayList characteristics = new ArrayList<>(service.getCharacteristics().size());
55 | for (BluetoothGattCharacteristic gattCharacteristic : service.getCharacteristics()) {
56 | characteristics.add(new Characteristic(this, gattCharacteristic));
57 | }
58 | return characteristics;
59 | }
60 |
61 | public WritableMap toJSObject() {
62 | WritableMap result = Arguments.createMap();
63 | result.putInt(Metadata.ID, id);
64 | result.putString(Metadata.UUID, UUIDConverter.fromUUID(service.getUuid()));
65 | result.putString(Metadata.DEVICE_ID, device.getNativeDevice().getMacAddress());
66 | result.putBoolean(Metadata.IS_PRIMARY, service.getType() == BluetoothGattService.SERVICE_TYPE_PRIMARY);
67 | return result;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Disabling obfuscation is useful if you collect stack traces from production crashes
20 | # (unless you are using a system that supports de-obfuscate the stack traces).
21 | -dontobfuscate
22 |
23 | # React Native
24 |
25 | # Keep our interfaces so they can be used by other ProGuard rules.
26 | # See http://sourceforge.net/p/proguard/bugs/466/
27 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip
28 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters
29 | -keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip
30 |
31 | # Do not strip any method/class that is annotated with @DoNotStrip
32 | -keep @com.facebook.proguard.annotations.DoNotStrip class *
33 | -keep @com.facebook.common.internal.DoNotStrip class *
34 | -keepclassmembers class * {
35 | @com.facebook.proguard.annotations.DoNotStrip *;
36 | @com.facebook.common.internal.DoNotStrip *;
37 | }
38 |
39 | -keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * {
40 | void set*(***);
41 | *** get*();
42 | }
43 |
44 | -keep class * extends com.facebook.react.bridge.JavaScriptModule { *; }
45 | -keep class * extends com.facebook.react.bridge.NativeModule { *; }
46 | -keepclassmembers,includedescriptorclasses class * { native ; }
47 | -keepclassmembers class * { @com.facebook.react.uimanager.UIProp ; }
48 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp ; }
49 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactPropGroup ; }
50 |
51 | -dontwarn com.facebook.react.**
52 |
53 | # okhttp
54 |
55 | -keepattributes Signature
56 | -keepattributes *Annotation*
57 | -keep class okhttp3.** { *; }
58 | -keep interface okhttp3.** { *; }
59 | -dontwarn okhttp3.**
60 |
61 | # okio
62 |
63 | -keep class sun.misc.Unsafe { *; }
64 | -dontwarn java.nio.file.*
65 | -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
66 | -dontwarn okio.**
67 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/reactnativeble/wrapper/Device.java:
--------------------------------------------------------------------------------
1 | package com.polidea.reactnativeble.wrapper;
2 |
3 | import com.facebook.react.bridge.Arguments;
4 | import com.facebook.react.bridge.WritableMap;
5 | import com.polidea.rxandroidble.RxBleConnection;
6 | import com.polidea.rxandroidble.RxBleDevice;
7 |
8 | import java.util.List;
9 | import java.util.UUID;
10 |
11 | public class Device {
12 |
13 | private interface Metadata {
14 | String ID = "id";
15 | String NAME = "name";
16 | String RSSI = "rssi";
17 |
18 | String MANUFACTURER_DATA = "manufacturerData";
19 | String SERVICE_DATA = "serviceData";
20 | String SERVICE_UUIDS = "serviceUUIDs";
21 | String TX_POWER_LEVEL = "txPowerLevel";
22 | String SOLICITED_SERVICE_UUIDS = "solicitedServiceUUIDs";
23 | String IS_CONNECTABLE = "isConnectable";
24 | String OVERFLOW_SERVICE_UUIDS = "overflowServiceUUIDs";
25 | }
26 |
27 | private RxBleDevice device;
28 | private RxBleConnection connection;
29 | private List services;
30 |
31 | public Device(RxBleDevice device, RxBleConnection connection) {
32 | this.device = device;
33 | this.connection = connection;
34 | }
35 |
36 | public void setServices(List services) {
37 | this.services = services;
38 | }
39 |
40 | public List getServices() {
41 | return services;
42 | }
43 |
44 | public RxBleDevice getNativeDevice() {
45 | return device;
46 | }
47 |
48 | public RxBleConnection getConnection() {
49 | return connection;
50 | }
51 |
52 | public Service getServiceByUUID(UUID uuid) {
53 | for(Service service : services) {
54 | if (uuid.equals(service.getNativeService().getUuid()))
55 | return service;
56 | }
57 | return null;
58 | }
59 |
60 | public WritableMap toJSObject(Integer rssi) {
61 | WritableMap result = Arguments.createMap();
62 | result.putString(Metadata.ID, device.getMacAddress());
63 | result.putString(Metadata.NAME, device.getName());
64 | if (rssi != null) {
65 | result.putInt(Metadata.RSSI, rssi);
66 | } else {
67 | result.putNull(Metadata.RSSI);
68 | }
69 |
70 | // Advertisement data is not set
71 | result.putNull(Metadata.MANUFACTURER_DATA);
72 | result.putNull(Metadata.SERVICE_DATA);
73 | result.putNull(Metadata.SERVICE_UUIDS);
74 | result.putNull(Metadata.TX_POWER_LEVEL);
75 | result.putNull(Metadata.SOLICITED_SERVICE_UUIDS);
76 | result.putNull(Metadata.IS_CONNECTABLE);
77 | result.putNull(Metadata.OVERFLOW_SERVICE_UUIDS);
78 |
79 | return result;
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/app/characteristics/CharacteristicView.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, { Component, PropTypes } from 'react';
4 | import { View, TouchableOpacity, Text, StyleSheet } from 'react-native';
5 |
6 | let borderColor = '#a2001d'
7 |
8 | const CharacteristicView = ({uuid, value, isReadable, isWritable, isNotifiable, onClick}) => {
9 | return (
10 |
11 |
12 |
13 |
14 | isReadable:
15 | {isReadable ? 'true' : 'false'}
16 |
17 |
18 | isWritable:
19 | {isWritable ? 'true' : 'false'}
20 |
21 |
22 | isNotifiable:
23 | {isNotifiable ? 'true' : 'false'}
24 |
25 |
26 |
27 |
28 | UUID:
29 | {uuid}
30 |
31 |
32 | Value:
33 | {value || '-'}
34 |
35 |
36 |
37 | )
38 | }
39 |
40 | CharacteristicView.propTypes = {
41 | isReadable: PropTypes.bool.isRequired,
42 | isWritable: PropTypes.bool.isRequired,
43 | isNotifiable: PropTypes.bool.isRequired,
44 | uuid: PropTypes.string.isRequired,
45 | onClick: PropTypes.func.isRequired
46 | }
47 |
48 | var styles = StyleSheet.create({
49 | background: {
50 | flexDirection: 'column',
51 | justifyContent: 'center',
52 | borderColor: borderColor,
53 | borderWidth: 2,
54 | overflow: 'hidden'
55 | },
56 | titleText: {
57 | fontWeight: 'bold',
58 | fontSize: 8,
59 | backgroundColor: '#ffd0d3',
60 | padding: 2
61 | },
62 | contentText: {
63 | paddingHorizontal: 2,
64 | paddingVertical: 3
65 | },
66 | uuidText: {
67 | paddingHorizontal: 2,
68 | paddingVertical: 3,
69 | fontSize: 10,
70 | },
71 | topRow: {
72 | flex: 1,
73 | flexDirection: 'row',
74 | backgroundColor: 'white',
75 | },
76 | bottomRow: {
77 | flex: 1,
78 | backgroundColor: 'white',
79 | },
80 | row: {
81 | flex: 1,
82 | }
83 | });
84 |
85 | export default CharacteristicView
--------------------------------------------------------------------------------
/__tests__/Device.js:
--------------------------------------------------------------------------------
1 | jest.mock('../src/BleManager')
2 | import { BleManager } from '../src/BleManager'
3 | import { Device } from '../src/Device'
4 |
5 | describe("Test if Device is properly calling BleManager's utility function: ", () => {
6 | const bleManager = new BleManager()
7 | const device = new Device({ id: 'id' }, bleManager)
8 |
9 | test('readRSSI', async () => {
10 | await device.readRSSI()
11 | expect(bleManager.readRSSIForDevice).toBeCalledWith('id', undefined)
12 | await device.readRSSI('transaction')
13 | expect(bleManager.readRSSIForDevice).toBeCalledWith('id', 'transaction')
14 | })
15 |
16 | test('connect', async () => {
17 | await device.connect({})
18 | expect(bleManager.connectToDevice).toBeCalledWith('id', {})
19 | })
20 |
21 | test('cancelConnection', async () => {
22 | await device.cancelConnection()
23 | expect(bleManager.cancelDeviceConnection).toBeCalledWith('id')
24 | })
25 |
26 | test('isConnected', async () => {
27 | await device.isConnected()
28 | expect(bleManager.isDeviceConnected).toBeCalledWith('id')
29 | })
30 |
31 | test('onDisconnected', async () => {
32 | const listener = jest.fn()
33 | await device.onDisconnected(listener)
34 | expect(bleManager.onDeviceDisconnected).toBeCalledWith('id', listener)
35 | })
36 |
37 | test('discoverAllServicesAndCharacteristics', async () => {
38 | await device.discoverAllServicesAndCharacteristics()
39 | expect(bleManager.discoverAllServicesAndCharacteristicsForDevice).toBeCalledWith('id')
40 | })
41 |
42 | test('services', async () => {
43 | await device.services()
44 | expect(bleManager.servicesForDevice).toBeCalledWith('id')
45 | })
46 |
47 | test('characteristicsForService', async () => {
48 | await device.characteristicsForService('aaaa')
49 | expect(bleManager.characteristicsForDevice).toBeCalledWith('id', 'aaaa')
50 | })
51 |
52 | test('readCharacteristicForService', async () => {
53 | await device.readCharacteristicForService('aaaa', 'bbbb', 'id')
54 | expect(bleManager.readCharacteristicForDevice).toBeCalledWith('id', 'aaaa', 'bbbb', 'id')
55 | })
56 |
57 | test('writeCharacteristicWithResponseForService', async () => {
58 | await device.writeCharacteristicWithResponseForService('aaaa', 'bbbb', 'value', 'id')
59 | expect(bleManager.writeCharacteristicWithResponseForDevice).toBeCalledWith('id', 'aaaa', 'bbbb', 'value', 'id')
60 | })
61 |
62 | test('writeCharacteristicWithoutResponseForService', async () => {
63 | await device.writeCharacteristicWithoutResponseForService('aaaa', 'bbbb', 'value', 'id')
64 | expect(bleManager.writeCharacteristicWithoutResponseForDevice).toBeCalledWith('id', 'aaaa', 'bbbb', 'value', 'id')
65 | })
66 |
67 | test('monitorCharacteristicForService', async () => {
68 | const listener = jest.fn()
69 | await device.monitorCharacteristicForService('aaaa', 'bbbb', listener, 'id')
70 | expect(bleManager.monitorCharacteristicForDevice).toBeCalledWith('id', 'aaaa', 'bbbb', listener, 'id')
71 | })
72 | })
73 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/reactnativeble/errors/ErrorConverter.java:
--------------------------------------------------------------------------------
1 | package com.polidea.reactnativeble.errors;
2 |
3 | import com.polidea.reactnativeble.exceptions.CannotMonitorCharacteristicException;
4 | import com.polidea.reactnativeble.utils.UUIDConverter;
5 | import com.polidea.rxandroidble.exceptions.BleAlreadyConnectedException;
6 | import com.polidea.rxandroidble.exceptions.BleCannotSetCharacteristicNotificationException;
7 | import com.polidea.rxandroidble.exceptions.BleCharacteristicNotFoundException;
8 | import com.polidea.rxandroidble.exceptions.BleGattCannotStartException;
9 | import com.polidea.rxandroidble.exceptions.BleGattException;
10 | import com.polidea.rxandroidble.exceptions.BleScanException;
11 |
12 | public class ErrorConverter {
13 |
14 | public Error toError(Throwable throwable) {
15 | if (throwable instanceof CannotMonitorCharacteristicException) {
16 | CannotMonitorCharacteristicException exception = (CannotMonitorCharacteristicException)throwable;
17 | String uuid = UUIDConverter.fromUUID(exception.getCharacteristic().getUuid());
18 | return BleError.cannotMonitorCharacteristic(uuid);
19 | }
20 | if (throwable instanceof BleScanException) {
21 | return toError((BleScanException) throwable);
22 | }
23 | if (throwable instanceof BleAlreadyConnectedException) {
24 | return new Error(throwable.toString(), 203);
25 | }
26 | if (throwable instanceof BleCharacteristicNotFoundException) {
27 | return new Error(throwable.toString(), 503);
28 | }
29 | if (throwable instanceof BleGattCannotStartException) {
30 | return new Error(throwable.toString(), 600);
31 | }
32 | if (throwable instanceof BleGattException) {
33 | return new Error(throwable.toString(), 700);
34 | }
35 | if (throwable instanceof BleCannotSetCharacteristicNotificationException) {
36 | return new Error(throwable.toString(), 403);
37 | }
38 | return new Error("Unknown error: " + throwable.toString(), 0);
39 | }
40 |
41 | private Error toError(BleScanException bleScanException) {
42 | final int reason = bleScanException.getReason();
43 | switch (reason) {
44 | case BleScanException.BLUETOOTH_CANNOT_START:
45 | return new Error("Bluetooth cannot start", 103);
46 | case BleScanException.BLUETOOTH_DISABLED:
47 | return new Error("Bluetooth is powered off", 102);
48 | case BleScanException.BLUETOOTH_NOT_AVAILABLE:
49 | return new Error("Bluetooth is not supported", 100);
50 | case BleScanException.LOCATION_PERMISSION_MISSING:
51 | return new Error("Bluetooth location permission is missing", 110);
52 | case BleScanException.LOCATION_SERVICES_DISABLED:
53 | return new Error("Bluetooth location services are disabled", 111);
54 | default:
55 | return new Error("Unknown scan error: " + bleScanException.toString(), 800);
56 | }
57 | }
58 | }
--------------------------------------------------------------------------------
/ios/BleClientManager/BleClientManager.xcodeproj/xcshareddata/xcschemes/BleClientManager.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
34 |
35 |
45 |
46 |
52 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
70 |
71 |
72 |
73 |
75 |
76 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/docs/assets/site.js:
--------------------------------------------------------------------------------
1 | /* global anchors */
2 |
3 | // add anchor links to headers
4 | anchors.options.placement = 'left';
5 | anchors.add('h3');
6 |
7 | // Filter UI
8 | var tocElements = document.getElementById('toc').getElementsByTagName('li');
9 |
10 | document.getElementById('filter-input').addEventListener('keyup', function(e) {
11 | var i, element, children;
12 |
13 | // enter key
14 | if (e.keyCode === 13) {
15 | // go to the first displayed item in the toc
16 | for (i = 0; i < tocElements.length; i++) {
17 | element = tocElements[i];
18 | if (!element.classList.contains('display-none')) {
19 | location.replace(element.firstChild.href);
20 | return e.preventDefault();
21 | }
22 | }
23 | }
24 |
25 | var match = function() {
26 | return true;
27 | };
28 |
29 | var value = this.value.toLowerCase();
30 |
31 | if (!value.match(/^\s*$/)) {
32 | match = function(element) {
33 | return element.firstChild.innerHTML.toLowerCase().indexOf(value) !== -1;
34 | };
35 | }
36 |
37 | for (i = 0; i < tocElements.length; i++) {
38 | element = tocElements[i];
39 | children = Array.from(element.getElementsByTagName('li'));
40 | if (match(element) || children.some(match)) {
41 | element.classList.remove('display-none');
42 | } else {
43 | element.classList.add('display-none');
44 | }
45 | }
46 | });
47 |
48 | var toggles = document.getElementsByClassName('toggle-step-sibling');
49 | for (var i = 0; i < toggles.length; i++) {
50 | toggles[i].addEventListener('click', toggleStepSibling);
51 | }
52 |
53 | function toggleStepSibling() {
54 | var stepSibling = this.parentNode.parentNode.parentNode.getElementsByClassName(
55 | 'toggle-target'
56 | )[0];
57 | var klass = 'display-none';
58 | if (stepSibling.classList.contains(klass)) {
59 | stepSibling.classList.remove(klass);
60 | stepSibling.innerHTML = '▾';
61 | } else {
62 | stepSibling.classList.add(klass);
63 | stepSibling.innerHTML = '▸';
64 | }
65 | }
66 |
67 | var items = document.getElementsByClassName('toggle-sibling');
68 | for (var j = 0; j < items.length; j++) {
69 | items[j].addEventListener('click', toggleSibling);
70 | }
71 |
72 | function toggleSibling() {
73 | var stepSibling = this.parentNode.getElementsByClassName('toggle-target')[0];
74 | var icon = this.getElementsByClassName('icon')[0];
75 | var klass = 'display-none';
76 | if (stepSibling.classList.contains(klass)) {
77 | stepSibling.classList.remove(klass);
78 | icon.innerHTML = '▾';
79 | } else {
80 | stepSibling.classList.add(klass);
81 | icon.innerHTML = '▸';
82 | }
83 | }
84 |
85 | function showHashTarget(targetId) {
86 | var hashTarget = document.getElementById(targetId);
87 | // new target is hidden
88 | if (
89 | hashTarget &&
90 | hashTarget.offsetHeight === 0 &&
91 | hashTarget.parentNode.parentNode.classList.contains('display-none')
92 | ) {
93 | hashTarget.parentNode.parentNode.classList.remove('display-none');
94 | }
95 | }
96 |
97 | window.addEventListener('hashchange', function() {
98 | showHashTarget(location.hash.substring(1));
99 | });
100 |
101 | showHashTarget(location.hash.substring(1));
102 |
103 | var toclinks = document.getElementsByClassName('pre-open');
104 | for (var k = 0; k < toclinks.length; k++) {
105 | toclinks[k].addEventListener('mousedown', preOpen, false);
106 | }
107 |
108 | function preOpen() {
109 | showHashTarget(this.hash.substring(1));
110 | }
111 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/app/ble/BleReducer.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as ble from './BleActions'
4 | import { Map, List, OrderedMap } from 'immutable'
5 |
6 | const defaultState = Map({
7 | devices: OrderedMap(),
8 | selecteddeviceIdentifier: null,
9 | selectedserviceUUID: null,
10 | selectedcharacteristicUUID: null,
11 | scanning: false,
12 | errors: List(),
13 | state: ble.DEVICE_STATE_DISCONNECTED,
14 | operations: Map(),
15 | transactionId: 0,
16 | });
17 |
18 | export default (state = defaultState, action) => {
19 | const transactionId = state.get('transactionId')
20 |
21 | switch (action.type) {
22 | case ble.START_SCAN:
23 | return state.set('scanning', true);
24 | case ble.STOP_SCAN:
25 | return state.set('scanning', false);
26 | case ble.DEVICE_FOUND:
27 | return state.mergeDeepIn(['devices', action.device.uuid], action.device);
28 | case ble.CHANGE_DEVICE_STATE:
29 | return state.withMutations(state => {
30 | state.set('scanning', false)
31 | .set('state', action.state)
32 | .set('selectedDeviceId', action.deviceIdentifier)
33 | });
34 | case ble.UPDATE_SERVICES:
35 | return state.mergeDeepIn(['devices', action.deviceIdentifier, 'services'], action.services);
36 | case ble.UPDATE_CHARACTERISTIC:
37 | return state.mergeDeepIn(['devices', action.deviceIdentifier,
38 | 'services', action.serviceUUID,
39 | 'characteristics', action.characteristicUUID], action.characteristic)
40 | case ble.SELECT_SERVICE:
41 | return state.set('selectedServiceId', action.serviceUUID);
42 | case ble.SELECT_CHARACTERISTIC:
43 | return state.set('selectedCharacteristicId', action.characteristicUUID);
44 | case ble.WRITE_CHARACTERISTIC:
45 | return state.withMutations(state => {
46 | state.setIn(['operations', transactionId], Map({
47 | type: 'write',
48 | state: 'new',
49 | deviceIdentifier: action.deviceIdentifier,
50 | serviceUUID: action.serviceUUID,
51 | characteristicUUID: action.characteristicUUID,
52 | base64Value: action.base64Value,
53 | transactionId
54 | })).set('transactionId', transactionId + 1);
55 | });
56 | case ble.READ_CHARACTERISTIC:
57 | return state.withMutations(state => {
58 | state.setIn(['operations', transactionId], Map({
59 | type: 'read',
60 | state: 'new',
61 | deviceIdentifier: action.deviceIdentifier,
62 | serviceUUID: action.serviceUUID,
63 | characteristicUUID: action.characteristicUUID,
64 | transactionId
65 | })).set('transactionId', transactionId + 1);
66 | });
67 | case ble.MONITOR_CHARACTERISTIC:
68 | const id = action.deviceIdentifier + " " + action.serviceUUID + " " + action.characteristicUUID
69 | if (!action.monitor) {
70 | return state.setIn(['operations', id, 'state'], 'cancel')
71 | }
72 | return state.setIn(['operations', id], Map({
73 | type: 'monitor',
74 | state: 'new',
75 | deviceIdentifier: action.deviceIdentifier,
76 | serviceUUID: action.serviceUUID,
77 | characteristicUUID: action.characteristicUUID,
78 | transactionId: id
79 | }));
80 | case ble.EXECUTE_TRANSACTION:
81 | return state.setIn(['operations', action.transactionId, 'state'], 'inProgress');
82 | case ble.COMPLETE_TRANSACTION:
83 | return state.removeIn(['operations', action.transactionId])
84 | case ble.PUSH_ERROR:
85 | return state.set('errors', state.get('errors').push(action.errorMessage))
86 | case ble.POP_ERROR:
87 | return state.set('errors', state.get('errors').pop())
88 | default:
89 | return state;
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/reactnativeble/converter/RxBleScanResultConverter.java:
--------------------------------------------------------------------------------
1 | package com.polidea.reactnativeble.converter;
2 |
3 | import android.util.Base64;
4 |
5 | import com.facebook.react.bridge.Arguments;
6 | import com.facebook.react.bridge.WritableArray;
7 | import com.facebook.react.bridge.WritableMap;
8 | import com.polidea.reactnativeble.advertisement.AdvertisementData;
9 | import com.polidea.reactnativeble.utils.UUIDConverter;
10 | import com.polidea.rxandroidble.RxBleScanResult;
11 |
12 | import java.util.Map;
13 | import java.util.UUID;
14 |
15 | public class RxBleScanResultConverter extends JSObjectConverter {
16 |
17 | interface Metadata {
18 | String ID = "id";
19 | String NAME = "name";
20 | String RSSI = "rssi";
21 |
22 | String MANUFACTURER_DATA = "manufacturerData";
23 | String SERVICE_DATA = "serviceData";
24 | String SERVICE_UUIDS = "serviceUUIDs";
25 | String TX_POWER_LEVEL = "txPowerLevel";
26 | String SOLICITED_SERVICE_UUIDS = "solicitedServiceUUIDs";
27 | String IS_CONNECTABLE = "isConnectable";
28 | String OVERFLOW_SERVICE_UUIDS = "overflowServiceUUIDs";
29 | }
30 |
31 | @Override
32 | public WritableMap toJSObject(RxBleScanResult value) {
33 | WritableMap result = Arguments.createMap();
34 | result.putString(Metadata.ID, value.getBleDevice().getMacAddress());
35 | result.putString(Metadata.NAME, value.getBleDevice().getName());
36 | result.putInt(Metadata.RSSI, value.getRssi());
37 |
38 | AdvertisementData advData = AdvertisementData.parseScanResponseData(value.getScanRecord());
39 | result.putString(Metadata.MANUFACTURER_DATA,
40 | advData.getManufacturerData() != null ?
41 | Base64.encodeToString(advData.getManufacturerData(), Base64.DEFAULT) :
42 | null);
43 |
44 | if (advData.getServiceData() != null) {
45 | WritableMap serviceData = Arguments.createMap();
46 | for (Map.Entry entry: advData.getServiceData().entrySet()) {
47 | serviceData.putString(UUIDConverter.fromUUID(entry.getKey()),
48 | Base64.encodeToString(entry.getValue(), Base64.DEFAULT));
49 | }
50 | result.putMap(Metadata.SERVICE_DATA, serviceData);
51 | } else {
52 | result.putNull(Metadata.SERVICE_DATA);
53 | }
54 |
55 | if (advData.getServiceUUIDs() != null) {
56 | WritableArray serviceUUIDs = Arguments.createArray();
57 | for (UUID serviceUUID : advData.getServiceUUIDs()) {
58 | serviceUUIDs.pushString(UUIDConverter.fromUUID(serviceUUID));
59 | }
60 | result.putArray(Metadata.SERVICE_UUIDS, serviceUUIDs);
61 | } else {
62 | result.putNull(Metadata.SERVICE_UUIDS);
63 | }
64 |
65 | if (advData.getTxPowerLevel() != null) {
66 | result.putInt(Metadata.TX_POWER_LEVEL, advData.getTxPowerLevel());
67 | } else {
68 | result.putNull(Metadata.TX_POWER_LEVEL);
69 | }
70 |
71 | if (advData.getSolicitedServiceUUIDs() != null) {
72 | WritableArray solicitedServiceUUIDs = Arguments.createArray();
73 | for (UUID serviceUUID : advData.getSolicitedServiceUUIDs()) {
74 | solicitedServiceUUIDs.pushString(UUIDConverter.fromUUID(serviceUUID));
75 | }
76 | result.putArray(Metadata.SOLICITED_SERVICE_UUIDS, solicitedServiceUUIDs);
77 | } else {
78 | result.putNull(Metadata.SOLICITED_SERVICE_UUIDS);
79 | }
80 |
81 | // Attributes which are not accessible on Android
82 | result.putNull(Metadata.IS_CONNECTABLE);
83 | result.putNull(Metadata.OVERFLOW_SERVICE_UUIDS);
84 |
85 | return result;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/ios/ReactBLEScanner/Base.lproj/LaunchScreen.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/INTRO.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | This guide is an introduction to BLE stack and APIs exported by this library. All examples
6 | will be based on CC2541 SensorTag.
7 |
8 | ## Creating BLE Manager
9 |
10 | First step is to create BleManager instance which is an entry point to all available APIs.
11 | Make sure to create it after application started its execution. For example we can do it in
12 | Component's constructor:
13 |
14 | ```js
15 | constructor() {
16 | super();
17 | this.manager = new BleManager();
18 | ...
19 | }
20 | ```
21 |
22 | Only one instance of BleManager is allowed. When you don't need any BLE functionality you
23 | can destroy created instance by calling `this.manager.destroy()` function. You can then
24 | recreate `BleManager` later on as we did above.
25 |
26 | ## Waiting for Powered On state
27 |
28 | When iOS application launches BLE stack is not immediately available and we need to check its status.
29 | To detect current state and following state changes we can use `onStateChange()` function:
30 |
31 | ```js
32 | componentWillMount() {
33 | const subscription = this.manager.onStateChange((state) => {
34 | if (state === 'PoweredOn') {
35 | this.scanAndConnect();
36 | subscription.remove();
37 | }
38 | }, true);
39 | }
40 | ```
41 |
42 | ## Scanning devices
43 |
44 | Devices needs to be scanned first to be able to connect to them. There is a simple function
45 | which allows only one callback to be registered to handle detected devices:
46 |
47 | ```js
48 | scanAndConnect() {
49 | this.manager.startDeviceScan(null, null, (error, device) => {
50 | if (error) {
51 | // Handle error (scanning will be stopped automatically)
52 | return
53 | }
54 |
55 | // Check if it is a device you are looking for based on advertisement data
56 | // or other criteria.
57 | if (device.name === 'TI BLE Sensor Tag' ||
58 | device.name === 'SensorTag') {
59 |
60 | // Stop scanning as it's not necessary if you are scanning for one device.
61 | this.manager.stopDeviceScan();
62 |
63 | // Proceed with connection.
64 | }
65 | });
66 | }
67 | ```
68 |
69 | It is worth to note that scanning function may emit one device multiple times. However
70 | when device is connected it won't broadcast and needs to be disconnected from central
71 | to be scanned again. Only one scanning listener can be registered.
72 |
73 | ## Connecting and discovering services and characteristics
74 |
75 | Once device is scanned it is in disconnected state. We need to connect to it and discover
76 | all services and characteristics it contains. Services may be understood
77 | as containers grouping characteristics based on their meaning. Characteristic is a
78 | container for a value which can be read, written and monitored based on available
79 | capabilities. For example connection may look like this:
80 |
81 | ```javascript
82 | device.connect()
83 | .then((device) => {
84 | return device.discoverAllServicesAndCharacteristics()
85 | })
86 | .then((device) => {
87 | // Do work on device with services and characteristics
88 | })
89 | .catch((error) => {
90 | // Handle errors
91 | });
92 | ```
93 |
94 | Discovery of services and characteristics is required to be executed only once. Additionally
95 | it can be a long process depending on number of characteristics and services available.
96 |
97 | ## Read, write and monitor values
98 |
99 | After successful discovery of services you can call
100 | * {@link #BleManager#readCharacteristicForDevice|BleManager.readCharacteristicForDevice()},
101 | * {@link #BleManager#writeCharacteristicWithResponseForDevice|BleManager.writeCharacteristicWithResponseForDevice()},
102 | * {@link #BleManager#monitorCharacteristicForDevice|BleManager.monitorCharacteristicForDevice()}
103 |
104 | and other functions which are described in detail in documentation.
105 |
106 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/app/ble/BleActions.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | export const START_SCAN = 'START_SCAN'
4 | export const STOP_SCAN = 'STOP_SCAN'
5 | export const DEVICE_FOUND = 'DEVICE_FOUND'
6 | export const CHANGE_DEVICE_STATE = 'CHANGE_DEVICE_STATE'
7 | export const WRITE_CHARACTERISTIC = 'WRITE_CHARACTERISTIC'
8 | export const READ_CHARACTERISTIC = 'READ_CHARACTERISTIC'
9 | export const MONITOR_CHARACTERISTIC = 'NOTIFY_CHARACTERISTIC'
10 | export const UPDATE_SERVICES = 'UPDATE_SERVICES'
11 | export const UPDATE_CHARACTERISTIC = 'UPDATE_CHARACTERISTIC'
12 | export const SELECT_SERVICE = 'SELECT_SERVICE'
13 | export const SELECT_CHARACTERISTIC = 'SELECT_CHARACTERISTIC'
14 | export const PUSH_ERROR = 'PUSH_ERROR'
15 | export const POP_ERROR = 'POP_ERROR'
16 | export const EXECUTE_TRANSACTION = 'EXECUTE_TRANSACTION'
17 | export const COMPLETE_TRANSACTION = 'COMPLETE_TRANSACTION'
18 |
19 | export function startScan() {
20 | return {
21 | type: START_SCAN
22 | }
23 | }
24 |
25 | export function stopScan() {
26 | return {
27 | type: STOP_SCAN
28 | }
29 | }
30 |
31 | export function deviceFound(device) {
32 | return {
33 | type: DEVICE_FOUND,
34 | device: device
35 | }
36 | }
37 |
38 | export function updateServices(deviceIdentifier, services) {
39 | return {
40 | type: UPDATE_SERVICES,
41 | deviceIdentifier: deviceIdentifier,
42 | services: services,
43 | }
44 | }
45 |
46 | export function updateCharacteristic(deviceIdentifier, serviceUUID, characteristicUUID, characteristic) {
47 | return {
48 | type: UPDATE_CHARACTERISTIC,
49 | deviceIdentifier,
50 | serviceUUID,
51 | characteristicUUID,
52 | characteristic
53 | }
54 | }
55 |
56 | export function writeCharacteristic(deviceIdentifier, serviceUUID, characteristicUUID, base64Value) {
57 | return {
58 | type: WRITE_CHARACTERISTIC,
59 | deviceIdentifier: deviceIdentifier,
60 | serviceUUID: serviceUUID,
61 | characteristicUUID: characteristicUUID,
62 | base64Value: base64Value
63 | }
64 | }
65 |
66 | export function readCharacteristic(deviceIdentifier, serviceUUID, characteristicUUID) {
67 | return {
68 | type: READ_CHARACTERISTIC,
69 | deviceIdentifier: deviceIdentifier,
70 | serviceUUID: serviceUUID,
71 | characteristicUUID: characteristicUUID
72 | }
73 | }
74 |
75 | export function monitorCharacteristic(deviceIdentifier, serviceUUID, characteristicUUID, monitor) {
76 | return {
77 | type: MONITOR_CHARACTERISTIC,
78 | deviceIdentifier,
79 | serviceUUID,
80 | characteristicUUID,
81 | monitor
82 | }
83 | }
84 |
85 | export const DEVICE_STATE_DISCONNECT = 'DISCONNECT'
86 | export const DEVICE_STATE_DISCONNECTING = 'DISCONNECTING'
87 | export const DEVICE_STATE_DISCONNECTED = 'DISCONNECTED'
88 | export const DEVICE_STATE_CONNECT = 'CONNECT'
89 | export const DEVICE_STATE_CONNECTING = 'CONNECTING'
90 | export const DEVICE_STATE_DISCOVERING = 'DISCOVERING'
91 | export const DEVICE_STATE_FETCHING = 'FETCHING SERVICES AND CHARACTERISTICS'
92 | export const DEVICE_STATE_CONNECTED = 'CONNECTED'
93 |
94 | export function changeDeviceState(deviceIdentifier, state) {
95 | return {
96 | type: CHANGE_DEVICE_STATE,
97 | deviceIdentifier: deviceIdentifier,
98 | state: state
99 | }
100 | }
101 |
102 | export function selectService(deviceIdentifier, serviceUUID) {
103 | return {
104 | type: SELECT_SERVICE,
105 | deviceIdentifier: deviceIdentifier,
106 | serviceUUID: serviceUUID
107 | }
108 | }
109 |
110 | export function selectCharacteristic(deviceIdentifier, serviceUUID, characteristicUUID) {
111 | return {
112 | type: SELECT_CHARACTERISTIC,
113 | deviceIdentifier: deviceIdentifier,
114 | serviceUUID: serviceUUID,
115 | characteristicUUID: characteristicUUID
116 | }
117 | }
118 |
119 | export function pushError(errorMessage) {
120 | return {
121 | type: PUSH_ERROR,
122 | errorMessage
123 | }
124 | }
125 |
126 | export function popError() {
127 | return {
128 | type: POP_ERROR,
129 | }
130 | }
131 |
132 | export function executeTransaction(transactionId) {
133 | return {
134 | type: EXECUTE_TRANSACTION,
135 | transactionId,
136 | }
137 | }
138 |
139 | export function completeTransaction(transactionId) {
140 | return {
141 | type: COMPLETE_TRANSACTION,
142 | transactionId
143 | }
144 | }
--------------------------------------------------------------------------------
/src/TypeDefinition.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | 'use strict'
3 |
4 | import { Device } from './Device'
5 |
6 | /**
7 | * Bluetooth device id.
8 | */
9 | export type DeviceId = string
10 |
11 | /**
12 | * Unique identifier for BLE objects.
13 | */
14 | export type Identifier = number
15 |
16 | /**
17 | * Bluetooth UUID
18 | */
19 | export type UUID = string
20 |
21 | /**
22 | * Base64 value
23 | */
24 | export type Base64 = string
25 |
26 | /**
27 | * Transaction identifier. All transaction identifiers in numeric form are reserved for internal use.
28 | */
29 | export type TransactionId = string
30 |
31 | /**
32 | * Subscription
33 | * @interface
34 | */
35 | export interface Subscription {
36 | /**
37 | * Removes subscription
38 | * @memberof Subscription
39 | * @ignore
40 | */
41 | remove(): void
42 | }
43 |
44 | /**
45 | * Options which can be passed to when creating BLE Manager
46 | */
47 | export interface BleManagerOptions {
48 | /**
49 | * BLE State restoration identifier used to restore state.
50 | * @memberof BleManagerOptions
51 | * @instance
52 | */
53 | restoreStateIdentifier?: string,
54 |
55 | /**
56 | * Optional function which is used to properly restore state of your BLE Manager. Callback
57 | * is emitted in the beginning of BleManager creation and optional {@link BleRestoreState}
58 | * is passed. When value is `null` application is launching for the first time, otherwise
59 | * it contains saved state which may be used by developer to continue working with
60 | * connected peripherals.
61 | * @memberof BleManagerOptions
62 | * @instance
63 | */
64 | restoreStateFunction?: (restoredState: ?BleRestoredState) => void
65 | }
66 |
67 | /**
68 | * Object representing information about restored BLE state after application relaunch.
69 | */
70 | export interface BleRestoredState {
71 | /**
72 | * List of connected devices after state restoration.
73 | * @type {Array}
74 | * @instance
75 | * @memberof BleRestoredState
76 | */
77 | connectedPeripherals: Array
78 | }
79 |
80 | /**
81 | * Options which can be passed to scanning function
82 | */
83 | export interface ScanOptions {
84 | /**
85 | * By allowing duplicates scanning records are received more frequently [iOS only]
86 | * @memberof ScanOptions
87 | * @instance
88 | */
89 | allowDuplicates?: boolean,
90 | /**
91 | * Whether to directly connect to the remote device (false) or to automatically connect as soon as the remote device
92 | * becomes available (true). [Android only]
93 | * @memberof ScanOptions
94 | * @instance
95 | */
96 | autoConnect?: boolean
97 | }
98 |
99 | /**
100 | * Connection specific options to be passed before connection happen. [Not used]
101 | */
102 | export interface ConnectionOptions {
103 | // Not used for now
104 | }
105 |
106 | /**
107 | * Device Bluetooth Low Energy state. It's keys are used to check {@link #BleManager#state} values
108 | * received by {@link BleManager}
109 | */
110 | export const State = {
111 | /**
112 | * The current state of the manager is unknown; an update is imminent.
113 | */
114 | Unknown: 'Unknown',
115 | /**
116 | * The connection with the system service was momentarily lost; an update is imminent.
117 | */
118 | Resetting: 'Resetting',
119 | /**
120 | * The platform does not support Bluetooth low energy.
121 | */
122 | Unsupported: 'Unsupported',
123 | /**
124 | * The app is not authorized to use Bluetooth low energy.
125 | */
126 | Unauthorized: 'Unauthorized',
127 | /**
128 | * Bluetooth is currently powered off.
129 | */
130 | PoweredOff: 'PoweredOff',
131 | /**
132 | * Bluetooth is currently powered on and available to use.
133 | */
134 | PoweredOn: 'PoweredOn'
135 | }
136 |
137 | /**
138 | * Native module logging log level. By default it is set to None.
139 | * @name LogLevel
140 | */
141 | export const LogLevel = {
142 | /**
143 | * Logging in native module is disabled
144 | */
145 | None: 'None',
146 | /**
147 | * All logs in native module are shown
148 | */
149 | Verbose: 'Verbose',
150 | /**
151 | * Only debug logs and of higher importance are shown in native module.
152 | */
153 | Debug: 'Debug',
154 | /**
155 | * Only info logs and of higher importance are shown in native module.
156 | */
157 | Info: 'Info',
158 | /**
159 | * Only warning logs and of higher importance are shown in native module.
160 | */
161 | Warning: 'Warning',
162 | /**
163 | * Only error logs and of higher importance are shown in native module.
164 | */
165 | Error: 'Error'
166 | }
167 |
--------------------------------------------------------------------------------
/docs/assets/fonts/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.
2 |
3 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
4 |
5 | This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
6 |
7 |
8 | -----------------------------------------------------------
9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
10 | -----------------------------------------------------------
11 |
12 | PREAMBLE
13 | The goals of the Open Font License (OFL) are to stimulate worldwide
14 | development of collaborative font projects, to support the font creation
15 | efforts of academic and linguistic communities, and to provide a free and
16 | open framework in which fonts may be shared and improved in partnership
17 | with others.
18 |
19 | The OFL allows the licensed fonts to be used, studied, modified and
20 | redistributed freely as long as they are not sold by themselves. The
21 | fonts, including any derivative works, can be bundled, embedded,
22 | redistributed and/or sold with any software provided that any reserved
23 | names are not used by derivative works. The fonts and derivatives,
24 | however, cannot be released under any other type of license. The
25 | requirement for fonts to remain under this license does not apply
26 | to any document created using the fonts or their derivatives.
27 |
28 | DEFINITIONS
29 | "Font Software" refers to the set of files released by the Copyright
30 | Holder(s) under this license and clearly marked as such. This may
31 | include source files, build scripts and documentation.
32 |
33 | "Reserved Font Name" refers to any names specified as such after the
34 | copyright statement(s).
35 |
36 | "Original Version" refers to the collection of Font Software components as
37 | distributed by the Copyright Holder(s).
38 |
39 | "Modified Version" refers to any derivative made by adding to, deleting,
40 | or substituting -- in part or in whole -- any of the components of the
41 | Original Version, by changing formats or by porting the Font Software to a
42 | new environment.
43 |
44 | "Author" refers to any designer, engineer, programmer, technical
45 | writer or other person who contributed to the Font Software.
46 |
47 | PERMISSION & CONDITIONS
48 | Permission is hereby granted, free of charge, to any person obtaining
49 | a copy of the Font Software, to use, study, copy, merge, embed, modify,
50 | redistribute, and sell modified and unmodified copies of the Font
51 | Software, subject to the following conditions:
52 |
53 | 1) Neither the Font Software nor any of its individual components,
54 | in Original or Modified Versions, may be sold by itself.
55 |
56 | 2) Original or Modified Versions of the Font Software may be bundled,
57 | redistributed and/or sold with any software, provided that each copy
58 | contains the above copyright notice and this license. These can be
59 | included either as stand-alone text files, human-readable headers or
60 | in the appropriate machine-readable metadata fields within text or
61 | binary files as long as those fields can be easily viewed by the user.
62 |
63 | 3) No Modified Version of the Font Software may use the Reserved Font
64 | Name(s) unless explicit written permission is granted by the corresponding
65 | Copyright Holder. This restriction only applies to the primary font name as
66 | presented to the users.
67 |
68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
69 | Software shall not be used to promote, endorse or advertise any
70 | Modified Version, except to acknowledge the contribution(s) of the
71 | Copyright Holder(s) and the Author(s) or with their explicit written
72 | permission.
73 |
74 | 5) The Font Software, modified or unmodified, in part or in whole,
75 | must be distributed entirely under this license, and must not be
76 | distributed under any other license. The requirement for fonts to
77 | remain under this license does not apply to any document created
78 | using the Font Software.
79 |
80 | TERMINATION
81 | This license becomes null and void if any of the above conditions are
82 | not met.
83 |
84 | DISCLAIMER
85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
93 | OTHER DEALINGS IN THE FONT SOFTWARE.
94 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/ios/Sniffator.xcodeproj/xcshareddata/xcschemes/Sniffator AdHoc.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
44 |
45 |
47 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
65 |
66 |
67 |
68 |
78 |
80 |
86 |
87 |
88 |
89 |
90 |
91 |
97 |
99 |
105 |
106 |
107 |
108 |
110 |
111 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/ios/Sniffator.xcodeproj/xcshareddata/xcschemes/Sniffator.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
44 |
45 |
47 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
65 |
66 |
67 |
68 |
78 |
80 |
86 |
87 |
88 |
89 |
90 |
91 |
97 |
99 |
105 |
106 |
107 |
108 |
110 |
111 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/app/characteristics/CharacteristicDetailsComponent.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | import React, { Component } from 'react';
3 | import { connect } from 'react-redux';
4 | import { View, Switch, Text, TextInput, StyleSheet } from 'react-native';
5 | import { Buffer } from 'buffer';
6 | import Style from '../view/Style';
7 | import ButtonView from '../view/ButtonView';
8 | import * as ble from '../ble/BleActions';
9 |
10 | class CharacteristicDetailsComponent extends Component {
11 |
12 | constructor() {
13 | super()
14 | this.state = { newValue: ''}
15 | }
16 |
17 | render() {
18 | const characteristic = this.props.characteristic;
19 | const uuid = characteristic.get('uuid')
20 | const isReadable = characteristic.get('isReadable')
21 | const isWritable = characteristic.get('isWritable')
22 | const isNotifiable = characteristic.get('isNotifiable')
23 | const isNotifying = characteristic.get('isNotifying')
24 | var value = characteristic.get('value')
25 | var valueAscii = '';
26 |
27 | if (value) {
28 | valueAscii = new Buffer(value, "base64").toString('ascii')
29 | value = new Buffer(value, "base64").toString('hex')
30 | } else {
31 | valueAscii = '-'
32 | value = '-'
33 | }
34 |
35 | const read = () => {
36 | this.props.readCharacteristic(this.props.deviceId,
37 | this.props.serviceId,
38 | this.props.characteristicId)
39 | }
40 |
41 | const write = () => {
42 | this.props.writeCharacteristic(this.props.deviceId,
43 | this.props.serviceId,
44 | this.props.characteristicId,
45 | new Buffer(this.state.newValue, 'hex').toString('base64'))
46 | }
47 |
48 | const notify = () => {
49 | this.props.monitorCharacteristic(this.props.deviceId,
50 | this.props.serviceId,
51 | this.props.characteristicId,
52 | !isNotifying)
53 | }
54 |
55 | return (
56 |
57 | UUID
58 | {uuid}
59 |
60 |
61 |
62 | isReadable:
63 | {isReadable ? 'true' : 'false'}
64 |
65 |
66 | isWritable:
67 | {isWritable ? 'true' : 'false'}
68 |
69 |
70 | isNotifiable:
71 | {isNotifiable ? 'true' : 'false'}
72 |
73 |
74 |
75 | Notify:
76 |
81 |
82 | Value HEX:
83 |
84 | {value}
85 |
86 |
87 |
88 | Value ASCII:
89 | {valueAscii}
90 |
91 | New value:
92 |
93 | this.setState({newValue: text})}
98 | />
99 |
100 |
101 |
102 | )
103 | }
104 | }
105 |
106 | const styles = StyleSheet.create({
107 | header: {
108 | paddingTop: 20,
109 | paddingBottom: 5,
110 | fontSize: 10,
111 | }
112 | });
113 |
114 | export default connect((state) => {
115 | const deviceId = state.getIn(['ble', 'selectedDeviceId'])
116 | const serviceId = state.getIn(['ble', 'selectedServiceId'])
117 | const characteristicId = state.getIn(['ble', 'selectedCharacteristicId'])
118 | return {
119 | deviceId,
120 | serviceId,
121 | characteristicId,
122 | characteristic: state.getIn(['ble', 'devices', deviceId, 'services', serviceId, 'characteristics', characteristicId])
123 | }
124 | },
125 | {
126 | readCharacteristic: ble.readCharacteristic,
127 | writeCharacteristic: ble.writeCharacteristic,
128 | monitorCharacteristic: ble.monitorCharacteristic
129 | }
130 | )(CharacteristicDetailsComponent)
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/reactnativeble/wrapper/Characteristic.java:
--------------------------------------------------------------------------------
1 | package com.polidea.reactnativeble.wrapper;
2 |
3 | import android.bluetooth.BluetoothGattCharacteristic;
4 | import android.bluetooth.BluetoothGattDescriptor;
5 | import android.util.Base64;
6 | import android.util.Pair;
7 |
8 | import com.facebook.react.bridge.Arguments;
9 | import com.facebook.react.bridge.WritableMap;
10 | import com.polidea.reactnativeble.utils.IdGenerator;
11 | import com.polidea.reactnativeble.utils.UUIDConverter;
12 | import com.polidea.rxandroidble.internal.RxBleLog;
13 |
14 | import java.util.UUID;
15 |
16 | public class Characteristic {
17 |
18 | private static final UUID CLIENT_CHARACTERISTIC_CONFIG_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
19 |
20 | private interface Metadata {
21 | String ID = "id";
22 | String UUID = "uuid";
23 | String SERVICE_ID = "serviceID";
24 | String SERVICE_UUID = "serviceUUID";
25 | String DEVICE_ID = "deviceID";
26 | String IS_READABLE = "isReadable";
27 | String IS_WRITABLE_WITH_RESPONSE = "isWritableWithResponse";
28 | String IS_WRITABLE_WITHOUT_RESPONSE = "isWritableWithoutResponse";
29 | String IS_NOTIFIABLE = "isNotifiable";
30 | String IS_NOTIFYING = "isNotifying";
31 | String IS_INDICTABLE = "isIndictable";
32 | String VALUE = "value";
33 | }
34 |
35 | private Service service;
36 | private BluetoothGattCharacteristic characteristic;
37 | private int id;
38 |
39 | public Characteristic(Service service, BluetoothGattCharacteristic characteristic) {
40 | this.service = service;
41 | this.characteristic = characteristic;
42 | this.id = IdGenerator.getIdForKey(new Pair<>(characteristic.getUuid(), characteristic.getInstanceId()));
43 | }
44 |
45 | public int getId() {
46 | return this.id;
47 | }
48 |
49 | public Service getService() {
50 | return service;
51 | }
52 |
53 | public BluetoothGattCharacteristic getNativeCharacteristic() {
54 | return characteristic;
55 | }
56 |
57 | public WritableMap toJSObject(byte[] value) {
58 | WritableMap js = Arguments.createMap();
59 |
60 | js.putInt(Metadata.ID, id);
61 | js.putString(Metadata.UUID, UUIDConverter.fromUUID(characteristic.getUuid()));
62 | js.putInt(Metadata.SERVICE_ID, service.getNativeService().getInstanceId());
63 | js.putString(Metadata.SERVICE_UUID, UUIDConverter.fromUUID(service.getNativeService().getUuid()));
64 | js.putString(Metadata.DEVICE_ID, service.getDevice().getNativeDevice().getMacAddress());
65 |
66 | js.putBoolean(Metadata.IS_READABLE, (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) != 0);
67 | js.putBoolean(Metadata.IS_WRITABLE_WITH_RESPONSE, (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) != 0);
68 | js.putBoolean(Metadata.IS_WRITABLE_WITHOUT_RESPONSE, (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) != 0);
69 | js.putBoolean(Metadata.IS_NOTIFIABLE, (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0);
70 | js.putBoolean(Metadata.IS_INDICTABLE, (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0);
71 |
72 | BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_UUID);
73 | boolean isNotifying = false;
74 | if (descriptor != null) {
75 | byte[] descriptorValue = descriptor.getValue();
76 | if (descriptorValue != null) {
77 | isNotifying = (descriptorValue[0] & 0x01) != 0;
78 | }
79 | }
80 | js.putBoolean(Metadata.IS_NOTIFYING, isNotifying);
81 |
82 | if (value == null) {
83 | value = characteristic.getValue();
84 | }
85 | js.putString(Metadata.VALUE, value != null ? Base64.encodeToString(value, Base64.DEFAULT) : null);
86 | return js;
87 | }
88 |
89 | private final static char[] hexArray = "0123456789ABCDEF".toCharArray();
90 |
91 | private static String bytesToHex(byte[] bytes) {
92 | char[] hexChars = new char[bytes.length * 2];
93 | for ( int j = 0; j < bytes.length; j++ ) {
94 | int v = bytes[j] & 0xFF;
95 | hexChars[j * 2] = hexArray[v >>> 4];
96 | hexChars[j * 2 + 1] = hexArray[v & 0x0F];
97 | }
98 | return new String(hexChars);
99 | }
100 |
101 | public void logValue(String message, byte[] value) {
102 | if (value == null) {
103 | value = characteristic.getValue();
104 | }
105 | String hexValue = value != null ? bytesToHex(value) : "(null)";
106 | RxBleLog.v(message +
107 | " Characteristic(uuid: " + characteristic.getUuid().toString() +
108 | ", id: " + id +
109 | ", value: " + hexValue + ")");
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/Characteristic.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | 'use strict'
3 |
4 | import { BleManager } from './BleManager'
5 | import type { NativeCharacteristic } from './BleModule'
6 | import type { DeviceId, Identifier, UUID, TransactionId, Base64, Subscription } from './TypeDefinition'
7 |
8 | /**
9 | * Characteristic object.
10 | */
11 | export class Characteristic implements NativeCharacteristic {
12 | /**
13 | * Internal BLE Manager handle
14 | * @private
15 | */
16 | _manager: BleManager
17 | /**
18 | * Characteristic unique identifier
19 | */
20 | id: Identifier
21 | /**
22 | * Characteristic UUID
23 | */
24 | uuid: UUID
25 | /**
26 | * Service's ID to which characteristic belongs
27 | */
28 | serviceID: Identifier
29 | /**
30 | * Service's UUID to which characteristic belongs
31 | */
32 | serviceUUID: UUID
33 | /**
34 | * Device's ID to which characteristic belongs
35 | */
36 | deviceID: DeviceId
37 | /**
38 | * True if characteristic can be read
39 | */
40 | isReadable: boolean
41 | /**
42 | * True if characteristic can be written with response
43 | */
44 | isWritableWithResponse: boolean
45 | /**
46 | * True if characteristic can be written without response
47 | */
48 | isWritableWithoutResponse: boolean
49 | /**
50 | * True if characteristic can monitor value changes.
51 | */
52 | isNotifiable: boolean
53 | /**
54 | * True if characteristic is monitoring value changes without ACK.
55 | */
56 | isNotifying: boolean
57 | /**
58 | * True if characteristic is monitoring value changes with ACK.
59 | */
60 | isIndictable: boolean
61 | /**
62 | * Characteristic value if present
63 | */
64 | value: ?Base64
65 |
66 | /**
67 | * Private constructor used to create instance of {@link Characteristic}.
68 | * @param {NativeCharacteristic} nativeCharacteristic NativeCharacteristic
69 | * @param {BleManager} manager BleManager
70 | * @private
71 | */
72 | constructor(nativeCharacteristic: NativeCharacteristic, manager: BleManager) {
73 | // $FlowFixMe Should be fixed in flow 0.46
74 | Object.assign(this, nativeCharacteristic, { _manager: manager })
75 | }
76 |
77 | /**
78 | * {@link #BleManager#readCharacteristicForDevice|bleManager.readCharacteristicForDevice()} with partially filled arguments.
79 | *
80 | * @param {?TransactionId} transactionId optional `transactionId` which can be used in
81 | * {@link #BleManager#cancelTransaction|bleManager.cancelTransaction()} function.
82 | * @returns {Promise} Promise which emits this {@link Characteristic}. Latest value will be stored
83 | * inside returned object.
84 | */
85 | read(transactionId: ?TransactionId): Promise {
86 | return this._manager._readCharacteristic(this.id, transactionId)
87 | }
88 |
89 | /**
90 | * {@link #BleManager#writeCharacteristicWithResponseForDevice|bleManager.writeCharacteristicWithResponseForDevice()} with partially filled arguments.
91 | *
92 | * @param {Base64} valueBase64 Value in Base64 format.
93 | * @param {?TransactionId} transactionId optional `transactionId` which can be used in
94 | * {@link #BleManager#cancelTransaction|bleManager.cancelTransaction()} function.
95 | * @returns {Promise} Promise which emits this {@link Characteristic}. Latest value may
96 | * not be stored inside returned object.
97 | */
98 | writeWithResponse(valueBase64: Base64, transactionId: ?TransactionId): Promise {
99 | return this._manager._writeCharacteristicWithResponse(this.id, valueBase64, transactionId)
100 | }
101 |
102 | /**
103 | * {@link #BleManager#writeCharacteristicWithoutResponseForDevice|bleManager.writeCharacteristicWithoutResponseForDevice()} with partially filled arguments.
104 | *
105 | * @param {Base64} valueBase64 Value in Base64 format.
106 | * @param {?TransactionId} transactionId optional `transactionId` which can be used in
107 | * {@link #BleManager#cancelTransaction|bleManager.cancelTransaction()} function.
108 | * @returns {Promise} Promise which emits this {@link Characteristic}. Latest value may
109 | * not be stored inside returned object.
110 | */
111 | writeWithoutResponse(valueBase64: Base64, transactionId: ?TransactionId): Promise {
112 | return this._manager._writeCharacteristicWithoutResponse(this.id, valueBase64, transactionId)
113 | }
114 |
115 | /**
116 | * {@link #BleManager#monitorCharacteristicForDevice|bleManager.monitorCharacteristicForDevice()} with partially filled arguments.
117 | *
118 | * @param {function(error: ?Error, characteristic: ?Characteristic)} listener callback which emits
119 | * this {@link Characteristic} with modified value for each notification.
120 | * @param {?TransactionId} transactionId optional `transactionId` which can be used in
121 | * {@link #BleManager#cancelTransaction|bleManager.cancelTransaction()} function.
122 | * @returns {Subscription} Subscription on which `remove()` function can be called to unsubscribe.
123 | */
124 | monitor(
125 | listener: (error: ?Error, characteristic: ?Characteristic) => void,
126 | transactionId: ?TransactionId
127 | ): Subscription {
128 | return this._manager._monitorCharacteristic(this.id, listener, transactionId)
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/ios/BleClientManager/BleExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BleExtensions.swift
3 | //
4 | // Created by Przemysław Lenart on 05/08/16.
5 | //
6 |
7 | import Foundation
8 | import RxBluetoothKit
9 | import CoreBluetooth
10 |
11 | extension RestoredState {
12 | var asJSObject: [AnyHashable: Any] {
13 | return [
14 | "connectedPeripherals": peripherals.map { $0.asJSObject() }
15 | ]
16 | }
17 | }
18 |
19 | extension ScannedPeripheral {
20 | var asJSObject: [AnyHashable: Any] {
21 | var serviceData: [String:String]?
22 | if let advServiceData = advertisementData.serviceData {
23 | var data = [String:String]()
24 | for (key, value) in advServiceData {
25 | data[key.fullUUIDString] = value.base64
26 | }
27 | serviceData = data
28 | }
29 |
30 | let manufacturerData = advertisementData
31 | .manufacturerData?
32 | .base64
33 |
34 | let serviceUUIDs = advertisementData
35 | .serviceUUIDs?
36 | .map { (uuid: CBUUID) in uuid.fullUUIDString }
37 |
38 | let overflowServiceUUIDs = advertisementData
39 | .overflowServiceUUIDs?
40 | .map { (uuid: CBUUID) in uuid.fullUUIDString }
41 |
42 | let solicitedServiceUUIDs = advertisementData
43 | .solicitedServiceUUIDs?
44 | .map { (uuid: CBUUID) in uuid.fullUUIDString }
45 |
46 | return [
47 | "id": peripheral.identifier.uuidString,
48 | "name": peripheral.name as Any,
49 | "rssi": rssi,
50 |
51 | "manufacturerData": manufacturerData as Any,
52 | "serviceData": serviceData as Any,
53 | "serviceUUIDs": serviceUUIDs as Any,
54 | "txPowerLevel": advertisementData.txPowerLevel as Any,
55 | "solicitedServiceUUIDs": solicitedServiceUUIDs as Any,
56 | "isConnectable": advertisementData.isConnectable as Any,
57 | "overflowServiceUUIDs": overflowServiceUUIDs as Any
58 | ]
59 | }
60 | }
61 |
62 | extension Peripheral {
63 | func asJSObject(withRssi: Int? = nil) -> [AnyHashable: Any] {
64 | return [
65 | "id": identifier.uuidString,
66 | "name": name as Any,
67 | "rssi": withRssi as Any,
68 |
69 | "manufacturerData": NSNull(),
70 | "serviceData": NSNull(),
71 | "serviceUUIDs": NSNull(),
72 | "txPowerLevel": NSNull(),
73 | "solicitedServiceUUIDs": NSNull(),
74 | "isConnectable": NSNull(),
75 | "overflowServiceUUIDs": NSNull()
76 | ]
77 | }
78 | }
79 |
80 | extension Service {
81 | var jsIdentifier: Double {
82 | return Double(UInt64(objectId) & ((1 << 53) - 1))
83 | }
84 | var asJSObject: [AnyHashable: Any] {
85 | return [
86 | "id": jsIdentifier,
87 | "uuid": uuid.fullUUIDString,
88 | "deviceID": peripheral.identifier.uuidString,
89 | "isPrimary": isPrimary
90 | ]
91 | }
92 | }
93 |
94 | extension Characteristic {
95 | var jsIdentifier: Double {
96 | return Double(UInt64(objectId) & ((1 << 53) - 1))
97 | }
98 | var asJSObject: [AnyHashable: Any] {
99 | return [
100 | "id": jsIdentifier,
101 | "uuid": uuid.fullUUIDString,
102 | "serviceID": service.jsIdentifier,
103 | "serviceUUID": service.uuid.fullUUIDString,
104 | "deviceID": service.peripheral.identifier.uuidString,
105 | "isReadable": properties.contains(.read),
106 | "isWritableWithResponse": properties.contains(.write),
107 | "isWritableWithoutResponse": properties.contains(.writeWithoutResponse),
108 | "isNotifiable": properties.contains(.notify),
109 | "isNotifying": isNotifying,
110 | "isIndictable": properties.contains(.indicate),
111 | "value": valueBase64 as Any
112 | ]
113 | }
114 | }
115 |
116 | extension RxBluetoothKitLog.LogLevel {
117 | var asJSObject: Any {
118 | switch self {
119 | case .none: return "None"
120 | case .verbose: return "Verbose"
121 | case .debug: return "Debug"
122 | case .info: return "Info"
123 | case .warning: return "Warning"
124 | case .error: return "Error"
125 | }
126 | }
127 |
128 | init(jsObject: String) {
129 | switch jsObject {
130 | case "Verbose": self = .verbose
131 | case "Debug": self = .debug
132 | case "Info": self = .info
133 | case "Warning": self = .warning
134 | case "Error": self = .error
135 | default:
136 | self = .none
137 | }
138 | }
139 | }
140 |
141 | extension BluetoothState {
142 | var asJSObject: Any {
143 | switch self {
144 | case .unknown: return "Unknown"
145 | case .resetting: return "Resetting"
146 | case .unsupported: return "Unsupported"
147 | case .unauthorized: return "Unauthorized"
148 | case .poweredOff: return "PoweredOff"
149 | case .poweredOn: return "PoweredOn"
150 | }
151 | }
152 | }
153 |
154 | extension Characteristic {
155 | var valueBase64: String? {
156 | return value?.base64
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/examples/ReactBLEScanner/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/src/Service.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | 'use strict'
3 |
4 | import { BleManager } from './BleManager'
5 | import { Characteristic } from './Characteristic'
6 | import type { NativeService } from './BleModule'
7 | import type { DeviceId, Identifier, Base64, UUID, Subscription, TransactionId } from './TypeDefinition'
8 |
9 | /**
10 | * Service object.
11 | */
12 | export class Service implements NativeService {
13 | /**
14 | * Internal BLE Manager handle
15 | * @private
16 | */
17 | _manager: BleManager
18 | /**
19 | * Service unique identifier
20 | */
21 | id: Identifier
22 | /**
23 | * Service UUID
24 | */
25 | uuid: UUID
26 | /**
27 | * Device's ID to which service belongs
28 | */
29 | deviceID: DeviceId
30 | /**
31 | * Value indicating whether the type of service is primary or secondary.
32 | */
33 | isPrimary: boolean
34 |
35 | /**
36 | * Private constructor used to create {@link Service} object.
37 | *
38 | * @param {NativeService} nativeService NativeService properties to be copied.
39 | * @param {BleManager} manager Current BleManager instance.
40 | * @private
41 | * @ignore
42 | */
43 | constructor(nativeService: NativeService, manager: BleManager) {
44 | // $FlowFixMe Should be fixed in flow 0.46
45 | Object.assign(this, nativeService, { _manager: manager })
46 | }
47 |
48 | /**
49 | * {@link #BleManager#characteristicsForDevice|bleManager.characteristicsForDevice()} with partially filled arguments.
50 | *
51 | * @returns {Promise>} Promise which emits array of {@link Characteristic} objects which are
52 | * discovered for this service.
53 | */
54 | characteristics(): Promise> {
55 | return this._manager._characteristicsForService(this.id)
56 | }
57 |
58 | /**
59 | * {@link #BleManager#readCharacteristicForDevice|bleManager.readCharacteristicForDevice()} with partially filled arguments.
60 | *
61 | * @param {UUID} characteristicUUID {@link Characteristic} UUID.
62 | * @param {?TransactionId} transactionId optional `transactionId` which can be used in
63 | * {@link #BleManager#cancelTransaction|bleManager.cancelTransaction()} function.
64 | * @returns {Promise} Promise which emits first {@link Characteristic} object matching specified
65 | * UUID path. Latest value of {@link Characteristic} will be stored inside returned object.
66 | */
67 | readCharacteristic(characteristicUUID: UUID, transactionId: ?TransactionId): Promise {
68 | return this._manager._readCharacteristicForService(this.id, characteristicUUID, transactionId)
69 | }
70 |
71 | /**
72 | * {@link #BleManager#writeCharacteristicWithResponseForDevice|bleManager.writeCharacteristicWithResponseForDevice()} with partially filled arguments.
73 | *
74 | * @param {UUID} characteristicUUID {@link Characteristic} UUID.
75 | * @param {Base64} valueBase64 Value in Base64 format.
76 | * @param {?TransactionId} transactionId optional `transactionId` which can be used in
77 | * {@link #BleManager#cancelTransaction|bleManager.cancelTransaction()} function.
78 | * @returns {Promise} Promise which emits first {@link Characteristic} object matching specified
79 | * UUID path. Latest value of characteristic may not be stored inside returned object.
80 | */
81 | writeCharacteristicWithResponse(
82 | characteristicUUID: UUID,
83 | valueBase64: Base64,
84 | transactionId: ?TransactionId
85 | ): Promise {
86 | return this._manager._writeCharacteristicWithResponseForService(
87 | this.id,
88 | characteristicUUID,
89 | valueBase64,
90 | transactionId
91 | )
92 | }
93 |
94 | /**
95 | * {@link #BleManager#writeCharacteristicWithoutResponseForDevice|bleManager.writeCharacteristicWithoutResponseForDevice()} with partially filled arguments.
96 | *
97 | * @param {UUID} characteristicUUID {@link Characteristic} UUID.
98 | * @param {Base64} valueBase64 Value in Base64 format.
99 | * @param {?TransactionId} transactionId optional `transactionId` which can be used in
100 | * {@link #BleManager#cancelTransaction|bleManager.cancelTransaction()} function.
101 | * @returns {Promise} Promise which emits first {@link Characteristic} object matching specified
102 | * UUID path. Latest value of characteristic may not be stored inside returned object.
103 | */
104 | writeCharacteristicWithoutResponse(
105 | characteristicUUID: UUID,
106 | valueBase64: Base64,
107 | transactionId: ?TransactionId
108 | ): Promise {
109 | return this._manager._writeCharacteristicWithoutResponseForService(
110 | this.id,
111 | characteristicUUID,
112 | valueBase64,
113 | transactionId
114 | )
115 | }
116 |
117 | /**
118 | * {@link #BleManager#monitorCharacteristicForDevice|bleManager.monitorCharacteristicForDevice()} with partially filled arguments.
119 | *
120 | * @param {UUID} characteristicUUID - {@link Characteristic} UUID.
121 | * @param {function(error: ?Error, characteristic: ?Characteristic)} listener callback which emits
122 | * {@link Characteristic} objects with modified value for each notification.
123 | * @param {?TransactionId} transactionId optional `transactionId` which can be used in
124 | * {@link #BleManager#cancelTransaction|bleManager.cancelTransaction()} function.
125 | * @returns {Subscription} Subscription on which `remove()` function can be called to unsubscribe.
126 | */
127 | monitorCharacteristic(
128 | characteristicUUID: UUID,
129 | listener: (error: ?Error, characteristic: ?Characteristic) => void,
130 | transactionId: ?TransactionId
131 | ): Subscription {
132 | return this._manager._monitorCharacteristicForService(this.id, characteristicUUID, listener, transactionId)
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | React Native Bluetooth Low Energy library using [RxBluetoothKit](https://github.com/Polidea/RxBluetoothKit) and [RxAndroidBle](https://github.com/Polidea/RxAndroidBle) as it's backend libraries.
7 |
8 | Example apps are available in [Google Play](https://play.google.com/store/apps/details?id=com.polidea.sniffator) and [App Store](https://itunes.apple.com/us/app/sniffator/id1147266354?ls=1&mt=8)!
9 |
10 | [](https://play.google.com/store/apps/details?id=com.polidea.sniffator) [](https://itunes.apple.com/us/app/sniffator/id1147266354?ls=1&mt=8)
11 |
12 | ---
13 |
14 | [](https://nodei.co/npm/react-native-ble-plx/)
15 |
16 | ---
17 |
18 | ## Recent Changes
19 |
20 | **0.6.0**
21 | - Added basic API to support background mode. When BleManager is constructed you can pass
22 | `restoreStateIdentifier` and `restoreStateFunction` to `BleManagerOptions` object to
23 | enable support for background mode. More info about usage can be found in documentation.
24 | - All subscriptions and promises are properly "Destroyed" when `destory()` function is called.
25 | - Fixed bug on Android where notification messages could be duplicated or skipped.
26 | - Updated RxAndroidBle to version 1.3
27 | - Updated README file.
28 | - Updated library logo
29 |
30 | [All previous changes](CHANGELOG.md)
31 |
32 | ## Documentation
33 |
34 | Documentation can be found [here](https://polidea.github.io/react-native-ble-plx/).
35 |
36 | ## Configuration & Installation
37 |
38 | ### Automatically
39 |
40 | ```bash
41 | npm install --save react-native-ble-plx
42 | react-native link
43 | ```
44 |
45 | Both on iOS and Android continue manually from step 7.
46 |
47 | ### Manually
48 |
49 | #### iOS
50 |
51 | 1) Add `react-native-ble-plx` to a project as a dependency in `package.json` file.
52 | For example `"react-native-ble-plx": "Polidea/react-native-ble-plx"` will install
53 | latest version from Polidea's Github repository.
54 | 2) Make sure that you have [Carthage](https://github.com/Carthage/Carthage) installed on your system.
55 | 3) Execute `npm install` to fetch and install a library.
56 | 4) Open iOS project located in `./ios` folder.
57 | 5) Move `BleClient.xcodeproj` located in `.node_modules/react-native-ble-plx/ios`
58 | using drag & drop to `Libraries` folder in your project.
59 | 6) In general settings of a target add `libBleClient.a` to Linked Frameworks and Libraries.
60 | 7) In `Build Settings`/`Search Paths`/`Framework search paths` add path: `$(SRCROOT)/../node_modules/react-native-ble-plx/ios/BleClientManager/Carthage/Build/iOS`.
61 | 8) In `Build Settings`/`Build Options`/`Always Embed Swift Standard Libraries` set to `Yes`.
62 | 9) In `Build Phases` click on top left button and add `New Run Script Phase`.
63 | * Shell command: `/usr/local/bin/carthage copy-frameworks`
64 | * Input Files:
65 | * `$(SRCROOT)/../node_modules/react-native-ble-plx/ios/BleClientManager/Carthage/Build/iOS/BleClientManager.framework`
66 | * `$(SRCROOT)/../node_modules/react-native-ble-plx/ios/BleClientManager/Carthage/Build/iOS/RxSwift.framework`
67 | * `$(SRCROOT)/../node_modules/react-native-ble-plx/ios/BleClientManager/Carthage/Build/iOS/RxBluetoothKit.framework`
68 | 10) Minimal supported version of iOS is 8.0
69 | 11) If you want to support background mode:
70 | * In your application target go to `Capabilities` tab and enable `Uses Bluetooth LE Accessories` in
71 | `Background Modes` section.
72 | * Pass `restoreStateIdentifier` and `restoreStateFunction` to `BleManager` constructor.
73 |
74 | #### Android
75 |
76 | 1) Add `react-native-ble-plx` to a project as a dependency in `package.json` file.
77 | For example `"react-native-ble-plx": "Polidea/react-native-ble-plx"` will install
78 | latest version from Polidea's Github repository.
79 | 2) Execute `npm install` to fetch and install a library.
80 | 3) Open Android project located in `./android` folder.
81 | 4) In `settings.gradle` add following lines:
82 | ```groovy
83 | include ':react-native-ble-plx'
84 | project(':react-native-ble-plx').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-ble-plx/android')
85 | ```
86 | 5) In `MainApplication.getPackages` import and add BleModule package:
87 | ```java
88 | import com.polidea.reactnativeble.BlePackage;
89 | ...
90 |
91 | public class MainApplication extends Application implements ReactApplication {
92 | ...
93 |
94 | @Override
95 | protected List getPackages() {
96 | return Arrays.asList(
97 | new MainReactPackage(),
98 | new BlePackage()
99 | );
100 | }
101 | ```
102 | 6) In `build.gradle` of `app` module add following dependency:
103 | ```groovy
104 | dependencies {
105 | ...
106 | compile project(':react-native-ble-plx')
107 | ...
108 | ```
109 | 7) Additionaly make sure that min SDK version is at least 18:
110 | ```groovy
111 | android {
112 | ...
113 | defaultConfig {
114 | minSdkVersion 18
115 | ...
116 | ```
117 |
118 |
119 | 8) In `AndroidManifest.xml`, add Bluetooth permissions and update ``:
120 |
121 | ```xml
122 |
125 |
126 |
127 |
128 |
131 |
132 |
133 |
122 | variant.outputs.each { output ->
123 | // For each separate APK per architecture, set a unique version code as described here:
124 | // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits
125 | def versionCodes = ["armeabi-v7a":1, "x86":2]
126 | def abi = output.getFilter(OutputFile.ABI)
127 | if (abi != null) { // null for the universal-debug, universal-release variants
128 | output.versionCodeOverride =
129 | versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
130 | }
131 | }
132 | }
133 | }
134 |
135 | dependencies {
136 | compile fileTree(dir: "libs", include: ["*.jar"])
137 | compile "com.android.support:appcompat-v7:23.0.1"
138 | compile "com.facebook.react:react-native:+" // From node_modules
139 | compile project(':react-native-ble-plx')
140 | compile project(':RNPermissionsModule')
141 | }
142 |
143 | // Run this once to be able to run the application with BUCK
144 | // puts all compile dependencies into folder libs for BUCK to use
145 | task copyDownloadableDepsToLibs(type: Copy) {
146 | from configurations.compile
147 | into 'libs'
148 | }
149 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/reactnativeble/advertisement/AdvertisementData.java:
--------------------------------------------------------------------------------
1 | package com.polidea.reactnativeble.advertisement;
2 |
3 | import java.nio.ByteBuffer;
4 | import java.nio.ByteOrder;
5 | import java.util.ArrayList;
6 | import java.util.HashMap;
7 | import java.util.Map;
8 | import java.util.UUID;
9 |
10 | public class AdvertisementData {
11 |
12 | private byte[] manufacturerData;
13 | private Map serviceData;
14 | private ArrayList serviceUUIDs;
15 | private Integer txPowerLevel;
16 | private ArrayList solicitedServiceUUIDs;
17 |
18 | private static final long BLUETOOTH_BASE_UUID_LSB = 0x800000805F9B34FBL;
19 | private static final int BLUETOOTH_BASE_UUID_MSB = 0x00001000;
20 |
21 | public byte[] getManufacturerData() {
22 | return manufacturerData;
23 | }
24 |
25 | public Map getServiceData() {
26 | return serviceData;
27 | }
28 |
29 | public ArrayList getServiceUUIDs() {
30 | return serviceUUIDs;
31 | }
32 |
33 | public Integer getTxPowerLevel() {
34 | return txPowerLevel;
35 | }
36 |
37 | public ArrayList getSolicitedServiceUUIDs() {
38 | return solicitedServiceUUIDs;
39 | }
40 |
41 | public static AdvertisementData parseScanResponseData(byte[] advertisement) {
42 | AdvertisementData advData = new AdvertisementData();
43 | ByteBuffer rawData = ByteBuffer.wrap(advertisement).order(ByteOrder.LITTLE_ENDIAN);
44 | while (rawData.remaining() >= 2) {
45 | int adLength = rawData.get() & 0xFF;
46 | if (adLength == 0) break;
47 | adLength -= 1;
48 | int adType = rawData.get() & 0xFF;
49 | if (rawData.remaining() < adLength) break;
50 | parseAdvertisementData(advData, adType, adLength, rawData.slice().order(ByteOrder.LITTLE_ENDIAN));
51 | rawData.position(rawData.position() + adLength);
52 | }
53 | return advData;
54 | }
55 |
56 | private static void parseAdvertisementData(AdvertisementData advData, int adType, int adLength, ByteBuffer data) {
57 | switch (adType) {
58 | case 0xFF:
59 | parseManufacturerData(advData, adLength, data);
60 | break;
61 |
62 | case 0x02:
63 | case 0x03:
64 | parseServiceUUIDs(advData, adLength, data, 2);
65 | break;
66 | case 0x04:
67 | case 0x05:
68 | parseServiceUUIDs(advData, adLength, data, 4);
69 | break;
70 | case 0x06:
71 | case 0x07:
72 | parseServiceUUIDs(advData, adLength, data, 16);
73 | break;
74 |
75 | case 0x0A:
76 | parseTxPowerLevel(advData, adLength, data);
77 | break;
78 |
79 | case 0x14:
80 | parseSolicitedServiceUUIDs(advData, adLength, data, 2);
81 | break;
82 | case 0x1F:
83 | parseSolicitedServiceUUIDs(advData, adLength, data, 4);
84 | break;
85 | case 0x15:
86 | parseSolicitedServiceUUIDs(advData, adLength, data, 16);
87 | break;
88 |
89 | case 0x16:
90 | parseServiceData(advData, adLength, data, 2);
91 | break;
92 | case 0x20:
93 | parseServiceData(advData, adLength, data, 4);
94 | break;
95 | case 0x21:
96 | parseServiceData(advData, adLength, data, 16);
97 | break;
98 | }
99 | }
100 |
101 | private static UUID parseUUID(ByteBuffer data, int uuidLength) {
102 | long lsb;
103 | long msb;
104 | switch (uuidLength) {
105 | case 2:
106 | msb = (((long) data.getShort() & 0xFFFF) << 32) + BLUETOOTH_BASE_UUID_MSB;
107 | lsb = BLUETOOTH_BASE_UUID_LSB;
108 | break;
109 | case 4:
110 | msb = ((long) data.getInt() << 32) + BLUETOOTH_BASE_UUID_MSB;
111 | lsb = BLUETOOTH_BASE_UUID_LSB;
112 | break;
113 | case 16:
114 | lsb = data.getLong();
115 | msb = data.getLong();
116 | break;
117 | default:
118 | data.position(data.position() + uuidLength);
119 | return null;
120 | }
121 | return new UUID(msb, lsb);
122 | }
123 |
124 | private static void parseSolicitedServiceUUIDs(AdvertisementData advData, int adLength, ByteBuffer data, int uuidLength) {
125 | if (advData.solicitedServiceUUIDs == null) advData.solicitedServiceUUIDs = new ArrayList<>();
126 | while (data.remaining() >= uuidLength && data.position() < adLength) {
127 | advData.solicitedServiceUUIDs.add(parseUUID(data, uuidLength));
128 | }
129 | }
130 |
131 | private static void parseServiceUUIDs(AdvertisementData advData, int adLength, ByteBuffer data, int uuidLength) {
132 | if (advData.serviceUUIDs == null) advData.serviceUUIDs = new ArrayList<>();
133 | while (data.remaining() >= uuidLength && data.position() < adLength) {
134 | advData.serviceUUIDs.add(parseUUID(data, uuidLength));
135 | }
136 | }
137 |
138 | private static void parseServiceData(AdvertisementData advData, int adLength, ByteBuffer data, int uuidLength) {
139 | if (adLength < uuidLength) return;
140 | if (advData.serviceData == null) advData.serviceData = new HashMap<>();
141 | UUID serviceUUID = parseUUID(data, uuidLength);
142 | int serviceDataLength = adLength - uuidLength;
143 | byte[] serviceData = new byte[serviceDataLength];
144 | data.get(serviceData, 0, serviceDataLength);
145 | advData.serviceData.put(serviceUUID, serviceData);
146 | }
147 |
148 | private static void parseTxPowerLevel(AdvertisementData advData, int adLength, ByteBuffer data) {
149 | if (adLength != 1) return;
150 | advData.txPowerLevel = (int)data.get();
151 | }
152 |
153 | private static void parseManufacturerData(AdvertisementData advData, int adLength, ByteBuffer data) {
154 | if (adLength < 2) return;
155 | advData.manufacturerData = new byte[adLength];
156 | data.get(advData.manufacturerData, 0, adLength);
157 | }
158 | }
159 |
--------------------------------------------------------------------------------