react-native-ble-mesh 1.1.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +439 -556
- package/docs/IOS-BACKGROUND-BLE.md +231 -0
- package/docs/OPTIMIZATION.md +70 -0
- package/docs/SPEC-v2.1.md +308 -0
- package/docs/prompt-instructions.md +528 -0
- package/package.json +2 -2
- package/src/MeshNetwork.js +659 -465
- package/src/constants/index.js +1 -0
- package/src/crypto/AutoCrypto.js +79 -0
- package/src/crypto/CryptoProvider.js +99 -0
- package/src/crypto/index.js +15 -63
- package/src/crypto/providers/ExpoCryptoProvider.js +125 -0
- package/src/crypto/providers/QuickCryptoProvider.js +134 -0
- package/src/crypto/providers/TweetNaClProvider.js +124 -0
- package/src/crypto/providers/index.js +11 -0
- package/src/errors/MeshError.js +2 -1
- package/src/expo/withBLEMesh.js +102 -0
- package/src/hooks/useMesh.js +30 -9
- package/src/hooks/useMessages.js +2 -0
- package/src/index.js +23 -8
- package/src/mesh/dedup/DedupManager.js +36 -10
- package/src/mesh/fragment/Assembler.js +5 -0
- package/src/mesh/index.js +1 -1
- package/src/mesh/monitor/ConnectionQuality.js +408 -0
- package/src/mesh/monitor/NetworkMonitor.js +327 -316
- package/src/mesh/monitor/index.js +7 -3
- package/src/mesh/peer/PeerManager.js +6 -1
- package/src/mesh/router/MessageRouter.js +26 -15
- package/src/mesh/router/RouteTable.js +7 -1
- package/src/mesh/store/StoreAndForwardManager.js +295 -297
- package/src/mesh/store/index.js +1 -1
- package/src/service/BatteryOptimizer.js +282 -278
- package/src/service/EmergencyManager.js +224 -214
- package/src/service/HandshakeManager.js +167 -13
- package/src/service/MeshService.js +72 -6
- package/src/service/SessionManager.js +77 -2
- package/src/service/audio/AudioManager.js +8 -2
- package/src/service/file/FileAssembler.js +106 -0
- package/src/service/file/FileChunker.js +79 -0
- package/src/service/file/FileManager.js +307 -0
- package/src/service/file/FileMessage.js +122 -0
- package/src/service/file/index.js +15 -0
- package/src/service/text/broadcast/BroadcastManager.js +16 -0
- package/src/transport/BLETransport.js +131 -9
- package/src/transport/MockTransport.js +1 -1
- package/src/transport/MultiTransport.js +305 -0
- package/src/transport/WiFiDirectTransport.js +295 -0
- package/src/transport/adapters/NodeBLEAdapter.js +34 -0
- package/src/transport/adapters/RNBLEAdapter.js +56 -1
- package/src/transport/index.js +6 -0
- package/src/utils/compression.js +291 -291
- package/src/crypto/aead.js +0 -189
- package/src/crypto/chacha20.js +0 -181
- package/src/crypto/hkdf.js +0 -187
- package/src/crypto/hmac.js +0 -143
- package/src/crypto/keys/KeyManager.js +0 -271
- package/src/crypto/keys/KeyPair.js +0 -216
- package/src/crypto/keys/SecureStorage.js +0 -219
- package/src/crypto/keys/index.js +0 -32
- package/src/crypto/noise/handshake.js +0 -410
- package/src/crypto/noise/index.js +0 -27
- package/src/crypto/noise/session.js +0 -253
- package/src/crypto/noise/state.js +0 -268
- package/src/crypto/poly1305.js +0 -113
- package/src/crypto/sha256.js +0 -240
- package/src/crypto/x25519.js +0 -154
package/README.md
CHANGED
|
@@ -1,737 +1,620 @@
|
|
|
1
1
|
# react-native-ble-mesh
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## Send messages without the internet. Like magic! ✨
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/react-native-ble-mesh)
|
|
6
|
+
[](https://www.npmjs.com/package/react-native-ble-mesh)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
[](https://www.typescriptlang.org/)
|
|
9
|
+
[](https://reactnative.dev/)
|
|
10
|
+
[]()
|
|
6
11
|
|
|
7
|
-
|
|
8
|
-
[](https://opensource.org/licenses/MIT)
|
|
12
|
+
---
|
|
9
13
|
|
|
10
|
-
##
|
|
14
|
+
## What is this?
|
|
11
15
|
|
|
12
|
-
|
|
13
|
-
- **Digital Signatures** - Ed25519 signatures for message authentication and identity verification
|
|
14
|
-
- **Multi-Hop Routing** - Messages can traverse multiple peers to reach their destination
|
|
15
|
-
- **Offline-First** - Works without internet connectivity using BLE
|
|
16
|
-
- **Forward Secrecy** - Automatic session rekeying for enhanced security
|
|
17
|
-
- **Replay Protection** - Sliding window algorithm prevents replay attacks
|
|
18
|
-
- **Traffic Analysis Resistance** - PKCS#7 padding to fixed block sizes
|
|
19
|
-
- **LZ4 Compression** - Efficient bandwidth usage for larger messages
|
|
20
|
-
- **Power Management** - Multiple power modes for battery optimization
|
|
21
|
-
- **Trust Management** - Peer verification, favorites, and blocking
|
|
22
|
-
- **Message Reliability** - Automatic retry with exponential backoff
|
|
16
|
+
Imagine you're at a concert, camping trip, or during a power outage — **no WiFi, no cell service**. How do you text your friends?
|
|
23
17
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
- Offline messaging apps (like Bridgefy, Briar)
|
|
27
|
-
- Disaster communication networks
|
|
28
|
-
- Privacy-focused P2P chat
|
|
29
|
-
- IoT mesh networks
|
|
30
|
-
- Gaming multiplayer over BLE
|
|
18
|
+
**This library lets phones talk to each other using Bluetooth!** Messages hop from phone to phone until they reach your friend — even if they're far away.
|
|
31
19
|
|
|
32
|
-
## Installation
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
npm install react-native-ble-mesh react-native-get-random-values react-native-ble-plx
|
|
36
|
-
cd ios && pod install
|
|
37
20
|
```
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
**IMPORTANT:** You must import `react-native-get-random-values` at the very top of your entry file (index.js or App.js) **before any other imports**:
|
|
42
|
-
|
|
43
|
-
```javascript
|
|
44
|
-
// index.js or App.js - This MUST be the first import!
|
|
45
|
-
import 'react-native-get-random-values';
|
|
46
|
-
|
|
47
|
-
// Now you can import other modules
|
|
48
|
-
import { AppRegistry } from 'react-native';
|
|
49
|
-
import App from './App';
|
|
50
|
-
import { name as appName } from './app.json';
|
|
51
|
-
|
|
52
|
-
AppRegistry.registerComponent(appName, () => App);
|
|
21
|
+
You Friend's Your
|
|
22
|
+
Phone ----Bluetooth---> Friend's ----Bluetooth---> Friend
|
|
23
|
+
Phone (300m away!)
|
|
53
24
|
```
|
|
54
25
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
### iOS Setup
|
|
26
|
+
**Think of it like a game of telephone, but for text messages and photos!**
|
|
58
27
|
|
|
59
|
-
|
|
28
|
+
---
|
|
60
29
|
|
|
61
|
-
|
|
62
|
-
<key>NSBluetoothAlwaysUsageDescription</key>
|
|
63
|
-
<string>This app uses Bluetooth to communicate with nearby devices</string>
|
|
64
|
-
<key>NSBluetoothPeripheralUsageDescription</key>
|
|
65
|
-
<string>This app uses Bluetooth to communicate with nearby devices</string>
|
|
66
|
-
<key>UIBackgroundModes</key>
|
|
67
|
-
<array>
|
|
68
|
-
<string>bluetooth-central</string>
|
|
69
|
-
<string>bluetooth-peripheral</string>
|
|
70
|
-
</array>
|
|
71
|
-
```
|
|
30
|
+
## See It In Action
|
|
72
31
|
|
|
73
|
-
|
|
32
|
+
```javascript
|
|
33
|
+
import { MeshNetwork } from 'react-native-ble-mesh';
|
|
74
34
|
|
|
75
|
-
|
|
35
|
+
// 1. Create your mesh network
|
|
36
|
+
const mesh = new MeshNetwork({ nickname: 'Alex' });
|
|
76
37
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
|
80
|
-
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
|
81
|
-
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
|
|
82
|
-
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
|
83
|
-
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
|
84
|
-
```
|
|
38
|
+
// 2. Start it up!
|
|
39
|
+
await mesh.start();
|
|
85
40
|
|
|
86
|
-
|
|
41
|
+
// 3. Send a message to everyone nearby
|
|
42
|
+
await mesh.broadcast('Hello everyone! 👋');
|
|
87
43
|
|
|
88
|
-
|
|
44
|
+
// 4. Listen for messages
|
|
45
|
+
mesh.on('messageReceived', (msg) => {
|
|
46
|
+
console.log(`${msg.from} says: ${msg.text}`);
|
|
47
|
+
});
|
|
48
|
+
```
|
|
89
49
|
|
|
90
|
-
|
|
91
|
-
const { MeshService, BLETransport } = require('react-native-ble-mesh');
|
|
50
|
+
**That's it! Four lines of code and you're chatting without internet!**
|
|
92
51
|
|
|
93
|
-
|
|
94
|
-
const mesh = new MeshService();
|
|
52
|
+
---
|
|
95
53
|
|
|
96
|
-
|
|
97
|
-
// Initialize with optional configuration
|
|
98
|
-
await mesh.initialize({
|
|
99
|
-
displayName: 'Alice',
|
|
100
|
-
storage: null, // Use in-memory storage
|
|
101
|
-
});
|
|
54
|
+
## Why Use This?
|
|
102
55
|
|
|
103
|
-
|
|
104
|
-
|
|
56
|
+
| Problem | Our Solution |
|
|
57
|
+
|---------|--------------|
|
|
58
|
+
| No WiFi or cell service | Works with just Bluetooth! |
|
|
59
|
+
| Friend is too far away | Messages hop through other phones |
|
|
60
|
+
| Need to send photos? | Send files & images up to 10MB! |
|
|
61
|
+
| Worried about privacy? | Encrypted with battle-tested crypto |
|
|
62
|
+
| Phone battery dying? | Smart power saving built-in |
|
|
63
|
+
| Need to delete everything fast? | One-tap emergency wipe |
|
|
64
|
+
| Using Expo? | Works out of the box! |
|
|
65
|
+
| Need faster transfers? | Wi-Fi Direct for big files |
|
|
66
|
+
| How's my connection? | Real-time signal quality indicator |
|
|
105
67
|
|
|
106
|
-
|
|
107
|
-
await mesh.start(transport);
|
|
68
|
+
---
|
|
108
69
|
|
|
109
|
-
|
|
110
|
-
console.log('My fingerprint:', mesh.getFingerprint());
|
|
111
|
-
}
|
|
70
|
+
## Cool Features
|
|
112
71
|
|
|
113
|
-
|
|
114
|
-
|
|
72
|
+
### 📡 Messages That Hop
|
|
73
|
+
Your message can jump through **up to 7 phones** to reach someone far away. If Alice can't reach Dave directly, the message goes: Alice → Bob → Carol → Dave!
|
|
115
74
|
|
|
116
|
-
###
|
|
75
|
+
### 📸 Send Photos & Files
|
|
76
|
+
Send pictures, documents, or any file up to 10MB. The library chops it into tiny pieces, sends them through the mesh, and puts them back together on the other side. You get a progress bar too!
|
|
117
77
|
|
|
118
78
|
```javascript
|
|
119
|
-
//
|
|
120
|
-
mesh.
|
|
121
|
-
|
|
122
|
-
|
|
79
|
+
// Send a photo to a friend
|
|
80
|
+
await mesh.sendFile('friend-id', {
|
|
81
|
+
data: photoBytes, // The file as bytes
|
|
82
|
+
name: 'vacation.jpg', // File name
|
|
83
|
+
mimeType: 'image/jpeg', // What kind of file
|
|
123
84
|
});
|
|
124
85
|
|
|
125
|
-
|
|
126
|
-
|
|
86
|
+
// Watch the progress
|
|
87
|
+
mesh.on('fileSendProgress', ({ name, percent }) => {
|
|
88
|
+
console.log(`Sending ${name}: ${percent}%`);
|
|
127
89
|
});
|
|
128
90
|
|
|
129
|
-
|
|
130
|
-
|
|
91
|
+
// Receive files from others
|
|
92
|
+
mesh.on('fileReceived', ({ from, file }) => {
|
|
93
|
+
console.log(`Got ${file.name} from ${from}!`);
|
|
94
|
+
// file.data has the bytes, file.mimeType tells you the type
|
|
131
95
|
});
|
|
132
96
|
```
|
|
133
97
|
|
|
134
|
-
###
|
|
98
|
+
### 📶 Connection Quality
|
|
99
|
+
See how good your connection is to each person — like signal bars on your phone!
|
|
135
100
|
|
|
136
101
|
```javascript
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
//
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Send a message to a channel
|
|
150
|
-
const channelMessageId = mesh.sendChannelMessage('general', 'Hello channel!');
|
|
102
|
+
const quality = mesh.getConnectionQuality('friend-id');
|
|
103
|
+
// quality.level = 'excellent' | 'good' | 'fair' | 'poor'
|
|
104
|
+
// quality.rssi = -55 (signal strength)
|
|
105
|
+
// quality.latencyMs = 45 (how fast, in milliseconds)
|
|
106
|
+
|
|
107
|
+
// Get alerted when connection changes
|
|
108
|
+
mesh.on('connectionQualityChanged', ({ peerId, level }) => {
|
|
109
|
+
if (level === 'poor') {
|
|
110
|
+
console.log('Connection getting weak! Move closer.');
|
|
111
|
+
}
|
|
112
|
+
});
|
|
151
113
|
```
|
|
152
114
|
|
|
153
|
-
###
|
|
115
|
+
### 📡 Wi-Fi Direct for Big Files
|
|
116
|
+
Bluetooth is great for messages, but slow for big files. Wi-Fi Direct is **250x faster**! The library automatically picks the best one:
|
|
117
|
+
|
|
118
|
+
- **Small message?** → Sends via Bluetooth (reliable, low power)
|
|
119
|
+
- **Big photo?** → Sends via Wi-Fi Direct (super fast)
|
|
120
|
+
- **Wi-Fi Direct not available?** → Falls back to Bluetooth automatically
|
|
154
121
|
|
|
155
122
|
```javascript
|
|
156
|
-
|
|
157
|
-
mesh.on('message', (message) => {
|
|
158
|
-
console.log('Received message:', message.content);
|
|
159
|
-
console.log('From:', message.senderId);
|
|
160
|
-
console.log('Type:', message.type);
|
|
161
|
-
});
|
|
123
|
+
import { MultiTransport } from 'react-native-ble-mesh';
|
|
162
124
|
|
|
163
|
-
//
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
125
|
+
// Use both Bluetooth AND Wi-Fi Direct together
|
|
126
|
+
const transport = new MultiTransport({
|
|
127
|
+
bleTransport: myBleTransport,
|
|
128
|
+
wifiTransport: myWifiTransport,
|
|
129
|
+
strategy: 'auto', // Let the library decide
|
|
167
130
|
});
|
|
168
131
|
|
|
169
|
-
|
|
170
|
-
mesh.
|
|
171
|
-
|
|
172
|
-
console.log('Content:', message.content);
|
|
173
|
-
});
|
|
132
|
+
const mesh = new MeshNetwork({ nickname: 'Alex' });
|
|
133
|
+
await mesh.start(transport);
|
|
134
|
+
// That's it! The library handles everything.
|
|
174
135
|
```
|
|
175
136
|
|
|
176
|
-
###
|
|
137
|
+
### 🔒 Secret Messages
|
|
138
|
+
Pick the encryption that works best for your app. The library auto-detects the fastest option:
|
|
177
139
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
140
|
+
| Option | Speed | Works On |
|
|
141
|
+
|--------|-------|----------|
|
|
142
|
+
| `react-native-quick-crypto` | ⚡ Blazing fast | React Native |
|
|
143
|
+
| `expo-crypto` + `tweetnacl` | 🚀 Fast | Expo apps |
|
|
144
|
+
| `tweetnacl` | ✅ Good | Everywhere |
|
|
183
145
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
146
|
+
```javascript
|
|
147
|
+
// The library picks the best one automatically!
|
|
148
|
+
const mesh = new MeshNetwork({
|
|
149
|
+
nickname: 'Alex',
|
|
150
|
+
crypto: 'auto', // Auto-detect fastest available
|
|
187
151
|
});
|
|
152
|
+
```
|
|
188
153
|
|
|
189
|
-
|
|
190
|
-
|
|
154
|
+
### 📬 Offline Delivery
|
|
155
|
+
Friend's phone turned off? No problem! Your message waits and delivers when they come back online.
|
|
191
156
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
157
|
+
### 🔋 Battery Friendly
|
|
158
|
+
Choose how much battery to use:
|
|
159
|
+
- **High Power** = Faster messages, more battery
|
|
160
|
+
- **Balanced** = Good speed, normal battery (default)
|
|
161
|
+
- **Low Power** = Slower messages, saves battery
|
|
162
|
+
- **Auto** = Let the phone decide based on battery level!
|
|
195
163
|
|
|
196
|
-
|
|
164
|
+
### 🚨 Panic Button
|
|
165
|
+
Triple-tap to instantly delete all messages and data. Everything gone in less than 0.2 seconds!
|
|
197
166
|
|
|
198
|
-
###
|
|
167
|
+
### 💬 Group Channels
|
|
168
|
+
Create chat rooms like `#camping-trip` or `#concert-squad`. Only people who join can see the messages!
|
|
199
169
|
|
|
200
|
-
|
|
170
|
+
---
|
|
201
171
|
|
|
202
|
-
|
|
172
|
+
## Installation
|
|
203
173
|
|
|
204
|
-
|
|
205
|
-
// Initialize the service
|
|
206
|
-
await mesh.initialize(options);
|
|
174
|
+
### Option A: Expo Projects (Easiest!) 🎯
|
|
207
175
|
|
|
208
|
-
|
|
209
|
-
|
|
176
|
+
```bash
|
|
177
|
+
npx expo install react-native-ble-mesh react-native-ble-plx react-native-get-random-values
|
|
178
|
+
```
|
|
210
179
|
|
|
211
|
-
|
|
212
|
-
await mesh.stop();
|
|
180
|
+
Add the plugin to your `app.json`:
|
|
213
181
|
|
|
214
|
-
|
|
215
|
-
|
|
182
|
+
```json
|
|
183
|
+
{
|
|
184
|
+
"expo": {
|
|
185
|
+
"plugins": [
|
|
186
|
+
["react-native-ble-mesh", {
|
|
187
|
+
"bluetoothAlwaysPermission": "Chat with nearby friends using Bluetooth"
|
|
188
|
+
}]
|
|
189
|
+
]
|
|
190
|
+
}
|
|
191
|
+
}
|
|
216
192
|
```
|
|
217
193
|
|
|
218
|
-
|
|
194
|
+
**That's it! The plugin handles all the permissions for you.** ✅
|
|
219
195
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
196
|
+
Then build your dev client:
|
|
197
|
+
```bash
|
|
198
|
+
npx expo prebuild
|
|
199
|
+
npx expo run:ios # or run:android
|
|
200
|
+
```
|
|
224
201
|
|
|
225
|
-
|
|
226
|
-
mesh.setDisplayName('Alice');
|
|
202
|
+
### Option B: Bare React Native
|
|
227
203
|
|
|
228
|
-
|
|
229
|
-
|
|
204
|
+
```bash
|
|
205
|
+
npm install react-native-ble-mesh react-native-ble-plx react-native-get-random-values
|
|
206
|
+
```
|
|
230
207
|
|
|
231
|
-
|
|
232
|
-
|
|
208
|
+
**iOS Setup:**
|
|
209
|
+
```bash
|
|
210
|
+
cd ios && pod install && cd ..
|
|
211
|
+
```
|
|
233
212
|
|
|
234
|
-
|
|
235
|
-
|
|
213
|
+
Add to `ios/YourApp/Info.plist`:
|
|
214
|
+
```xml
|
|
215
|
+
<key>NSBluetoothAlwaysUsageDescription</key>
|
|
216
|
+
<string>Chat with nearby friends using Bluetooth</string>
|
|
217
|
+
<key>NSBluetoothPeripheralUsageDescription</key>
|
|
218
|
+
<string>Chat with nearby friends using Bluetooth</string>
|
|
219
|
+
<key>UIBackgroundModes</key>
|
|
220
|
+
<array>
|
|
221
|
+
<string>bluetooth-central</string>
|
|
222
|
+
<string>bluetooth-peripheral</string>
|
|
223
|
+
</array>
|
|
224
|
+
```
|
|
236
225
|
|
|
237
|
-
|
|
238
|
-
|
|
226
|
+
**Android Setup:** Add to `android/app/src/main/AndroidManifest.xml`:
|
|
227
|
+
```xml
|
|
228
|
+
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
|
229
|
+
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
|
|
230
|
+
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
|
231
|
+
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
|
239
232
|
```
|
|
240
233
|
|
|
241
|
-
|
|
234
|
+
### Final Step (Both Options)
|
|
242
235
|
|
|
243
|
-
|
|
244
|
-
// Get all known peers
|
|
245
|
-
const peers = mesh.getPeers();
|
|
236
|
+
Add this as the **very first line** in your app:
|
|
246
237
|
|
|
247
|
-
|
|
248
|
-
|
|
238
|
+
```javascript
|
|
239
|
+
import 'react-native-get-random-values';
|
|
240
|
+
```
|
|
249
241
|
|
|
250
|
-
|
|
251
|
-
await mesh.initiateHandshake(peerId);
|
|
242
|
+
### Optional: Extra Speed & Features
|
|
252
243
|
|
|
253
|
-
|
|
254
|
-
|
|
244
|
+
```bash
|
|
245
|
+
# Want encryption? Pick one:
|
|
246
|
+
npm install tweetnacl # Works everywhere
|
|
247
|
+
npm install react-native-quick-crypto # Fastest (native)
|
|
255
248
|
|
|
256
|
-
|
|
257
|
-
|
|
249
|
+
# Want Wi-Fi Direct for big file transfers?
|
|
250
|
+
npm install react-native-wifi-p2p
|
|
258
251
|
```
|
|
259
252
|
|
|
260
|
-
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## Quick Start Examples
|
|
256
|
+
|
|
257
|
+
### Example 1: Simple Chat
|
|
261
258
|
|
|
262
259
|
```javascript
|
|
263
|
-
|
|
264
|
-
mesh.verifyPeer(peerId, fingerprint);
|
|
260
|
+
import { MeshNetwork } from 'react-native-ble-mesh';
|
|
265
261
|
|
|
266
|
-
|
|
267
|
-
mesh.
|
|
262
|
+
const mesh = new MeshNetwork({ nickname: 'YourName' });
|
|
263
|
+
await mesh.start();
|
|
268
264
|
|
|
269
|
-
//
|
|
270
|
-
mesh.
|
|
265
|
+
// Send message to everyone
|
|
266
|
+
await mesh.broadcast('Hi everyone!');
|
|
271
267
|
|
|
272
|
-
//
|
|
273
|
-
mesh.
|
|
268
|
+
// Send private message to one person
|
|
269
|
+
await mesh.sendDirect('friend-id', 'Hey, just for you!');
|
|
274
270
|
|
|
275
|
-
//
|
|
276
|
-
|
|
271
|
+
// Receive messages
|
|
272
|
+
mesh.on('messageReceived', ({ from, text }) => {
|
|
273
|
+
console.log(`${from}: ${text}`);
|
|
274
|
+
});
|
|
277
275
|
|
|
278
|
-
|
|
279
|
-
const favorites = mesh.getFavoritePeers();
|
|
276
|
+
await mesh.stop();
|
|
280
277
|
```
|
|
281
278
|
|
|
282
|
-
|
|
279
|
+
### Example 2: Send a Photo
|
|
283
280
|
|
|
284
281
|
```javascript
|
|
285
|
-
//
|
|
286
|
-
mesh.
|
|
282
|
+
// Send a photo
|
|
283
|
+
await mesh.sendFile('friend-id', {
|
|
284
|
+
data: imageBytes,
|
|
285
|
+
name: 'selfie.jpg',
|
|
286
|
+
mimeType: 'image/jpeg',
|
|
287
|
+
});
|
|
287
288
|
|
|
288
|
-
//
|
|
289
|
-
|
|
289
|
+
// Track sending progress
|
|
290
|
+
mesh.on('fileSendProgress', ({ percent }) => {
|
|
291
|
+
console.log(`${percent}% sent`);
|
|
292
|
+
});
|
|
290
293
|
|
|
291
|
-
//
|
|
292
|
-
mesh.
|
|
294
|
+
// Receive photos
|
|
295
|
+
mesh.on('fileReceived', ({ from, file }) => {
|
|
296
|
+
console.log(`Got ${file.name} (${file.size} bytes) from ${from}`);
|
|
297
|
+
// file.data = the photo bytes
|
|
298
|
+
// file.mimeType = 'image/jpeg'
|
|
299
|
+
});
|
|
293
300
|
```
|
|
294
301
|
|
|
295
|
-
|
|
302
|
+
### Example 3: Group Channels
|
|
296
303
|
|
|
297
304
|
```javascript
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
// Get current state
|
|
302
|
-
const state = mesh.getState();
|
|
303
|
-
|
|
304
|
-
// Get detailed statistics
|
|
305
|
-
const stats = mesh.getStats();
|
|
305
|
+
await mesh.joinChannel('#road-trip');
|
|
306
|
+
await mesh.sendToChannel('#road-trip', 'Are we there yet?');
|
|
307
|
+
await mesh.leaveChannel('#road-trip');
|
|
306
308
|
```
|
|
307
309
|
|
|
308
|
-
###
|
|
309
|
-
|
|
310
|
-
| Event | Description | Payload |
|
|
311
|
-
|-------|-------------|---------|
|
|
312
|
-
| `peer-discovered` | New peer found | `{ peer }` |
|
|
313
|
-
| `peer-connected` | Connected to peer | `{ peer }` |
|
|
314
|
-
| `peer-disconnected` | Disconnected from peer | `{ peer }` |
|
|
315
|
-
| `peer-secured` | Secure session established | `{ peer }` |
|
|
316
|
-
| `message` | Any message received | `{ message }` |
|
|
317
|
-
| `private-message` | Private message received | `{ message }` |
|
|
318
|
-
| `channel-message` | Channel message received | `{ message }` |
|
|
319
|
-
| `message-delivered` | Message delivery confirmed | `{ messageId, peerId, timestamp }` |
|
|
320
|
-
| `message-failed` | Message delivery failed | `{ messageId, peerId, error }` |
|
|
321
|
-
| `read-receipt` | Read receipt received | `{ messageIds, fromPeerId, timestamp }` |
|
|
322
|
-
| `identity-announced` | Peer announced identity | `{ peerId, fingerprint, nickname }` |
|
|
323
|
-
| `peer-verified` | Peer verification changed | `{ peerId, fingerprint }` |
|
|
324
|
-
| `peer-compromised` | Fingerprint mismatch detected | `{ peerId, expected, actual }` |
|
|
325
|
-
|
|
326
|
-
## Configuration
|
|
327
|
-
|
|
328
|
-
### Initialization Options
|
|
310
|
+
### Example 4: Check Connection Quality
|
|
329
311
|
|
|
330
312
|
```javascript
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
// Enable compression for messages > 64 bytes
|
|
342
|
-
compressionEnabled: true,
|
|
343
|
-
|
|
344
|
-
// Compression threshold in bytes
|
|
345
|
-
compressionThreshold: 64,
|
|
313
|
+
// How's my connection to a friend?
|
|
314
|
+
const quality = mesh.getConnectionQuality('friend-id');
|
|
315
|
+
console.log(`Signal: ${quality.level}`); // excellent, good, fair, poor
|
|
316
|
+
console.log(`Speed: ${quality.latencyMs}ms`);
|
|
317
|
+
|
|
318
|
+
// Check everyone at once
|
|
319
|
+
const all = mesh.getAllConnectionQuality();
|
|
320
|
+
all.forEach(q => {
|
|
321
|
+
console.log(`${q.peerId}: ${q.level} (${q.transport})`);
|
|
346
322
|
});
|
|
347
323
|
```
|
|
348
324
|
|
|
349
|
-
###
|
|
350
|
-
|
|
351
|
-
| Mode | Scan Interval | Window | Use Case |
|
|
352
|
-
|------|---------------|--------|----------|
|
|
353
|
-
| `PERFORMANCE` | 1s | 800ms | Active foreground use |
|
|
354
|
-
| `BALANCED` | 5s | 2s | Default mode |
|
|
355
|
-
| `POWER_SAVER` | 30s | 5s | Background operation |
|
|
356
|
-
| `ULTRA_POWER_SAVER` | 60s | 5s | Minimal battery drain |
|
|
357
|
-
|
|
358
|
-
### Message Options
|
|
325
|
+
### Example 5: Save Battery
|
|
359
326
|
|
|
360
327
|
```javascript
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
//
|
|
364
|
-
requiresAck: true,
|
|
365
|
-
|
|
366
|
-
// Enable compression
|
|
367
|
-
compress: true,
|
|
368
|
-
|
|
369
|
-
// Maximum hop count (1-15)
|
|
370
|
-
maxHops: 7,
|
|
371
|
-
|
|
372
|
-
// Priority level
|
|
373
|
-
priority: 'high', // 'normal' | 'high'
|
|
328
|
+
const mesh = new MeshNetwork({
|
|
329
|
+
nickname: 'Smart',
|
|
330
|
+
batteryMode: 'auto', // Adjusts based on your battery level!
|
|
374
331
|
});
|
|
375
332
|
```
|
|
376
333
|
|
|
377
|
-
|
|
334
|
+
### Example 6: Emergency Delete
|
|
378
335
|
|
|
379
|
-
|
|
336
|
+
```javascript
|
|
337
|
+
mesh.enablePanicMode({ trigger: 'triple_tap' });
|
|
380
338
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
- **Key Derivation**: HKDF
|
|
339
|
+
// Or wipe everything right now
|
|
340
|
+
await mesh.wipeAllData();
|
|
341
|
+
// All messages, keys, and data = GONE! 💨
|
|
342
|
+
```
|
|
386
343
|
|
|
387
|
-
|
|
344
|
+
---
|
|
388
345
|
|
|
389
|
-
|
|
346
|
+
## Using React Hooks
|
|
390
347
|
|
|
348
|
+
```javascript
|
|
349
|
+
import { useMesh, useMessages, usePeers } from 'react-native-ble-mesh/hooks';
|
|
350
|
+
import { BLETransport } from 'react-native-ble-mesh';
|
|
351
|
+
|
|
352
|
+
function ChatScreen() {
|
|
353
|
+
const { mesh, state, initialize, destroy } = useMesh({ displayName: 'Alex' });
|
|
354
|
+
const { messages, sendBroadcast } = useMessages(mesh);
|
|
355
|
+
const { peers, connectedCount } = usePeers(mesh);
|
|
356
|
+
|
|
357
|
+
useEffect(() => {
|
|
358
|
+
initialize(new BLETransport());
|
|
359
|
+
return () => destroy();
|
|
360
|
+
}, []);
|
|
361
|
+
|
|
362
|
+
if (state !== 'active') return <Text>Starting mesh...</Text>;
|
|
363
|
+
|
|
364
|
+
return (
|
|
365
|
+
<View>
|
|
366
|
+
<Text>Connected to {connectedCount} people</Text>
|
|
367
|
+
{messages.map(msg => (
|
|
368
|
+
<Text key={msg.id}>{msg.senderId}: {msg.content}</Text>
|
|
369
|
+
))}
|
|
370
|
+
<Button title="Say Hi!" onPress={() => sendBroadcast('Hello!')} />
|
|
371
|
+
</View>
|
|
372
|
+
);
|
|
373
|
+
}
|
|
391
374
|
```
|
|
392
|
-
-> e
|
|
393
|
-
<- e, ee, s, es
|
|
394
|
-
-> s, se
|
|
395
|
-
```
|
|
396
|
-
|
|
397
|
-
This provides:
|
|
398
|
-
- Mutual authentication
|
|
399
|
-
- Forward secrecy
|
|
400
|
-
- Identity hiding
|
|
401
|
-
|
|
402
|
-
### Session Security
|
|
403
375
|
|
|
404
|
-
|
|
405
|
-
- **Replay Protection**: Sliding window algorithm with configurable window size
|
|
406
|
-
- **Nonce Management**: Automatic nonce tracking prevents reuse
|
|
376
|
+
---
|
|
407
377
|
|
|
408
|
-
|
|
378
|
+
## Everything You Can Do
|
|
409
379
|
|
|
410
|
-
|
|
380
|
+
### Starting & Stopping
|
|
411
381
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
382
|
+
| Method | What It Does |
|
|
383
|
+
|--------|--------------|
|
|
384
|
+
| `mesh.start()` | Turn on the mesh network |
|
|
385
|
+
| `mesh.stop()` | Turn it off (can restart later) |
|
|
386
|
+
| `mesh.destroy()` | Completely shut down |
|
|
416
387
|
|
|
417
|
-
|
|
418
|
-
mesh.verifyPeer(peerId, theirFingerprint);
|
|
388
|
+
### Sending Messages
|
|
419
389
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
390
|
+
| Method | What It Does |
|
|
391
|
+
|--------|--------------|
|
|
392
|
+
| `mesh.broadcast('Hi!')` | Send to everyone nearby |
|
|
393
|
+
| `mesh.sendDirect(id, 'Hey')` | Private message to one person |
|
|
394
|
+
| `mesh.sendToChannel('#fun', 'Yo')` | Send to a group |
|
|
424
395
|
|
|
425
|
-
|
|
396
|
+
### Sending Files
|
|
426
397
|
|
|
427
|
-
|
|
398
|
+
| Method | What It Does |
|
|
399
|
+
|--------|--------------|
|
|
400
|
+
| `mesh.sendFile(id, { data, name, mimeType })` | Send a file to someone |
|
|
401
|
+
| `mesh.getActiveTransfers()` | See files being sent/received |
|
|
402
|
+
| `mesh.cancelTransfer(id)` | Cancel a file transfer |
|
|
428
403
|
|
|
429
|
-
|
|
404
|
+
### Connection Quality
|
|
430
405
|
|
|
431
|
-
|
|
|
432
|
-
|
|
433
|
-
|
|
|
434
|
-
|
|
|
435
|
-
| flags | 1 | Feature flags |
|
|
436
|
-
| ttl | 1 | Hop count + max hops |
|
|
437
|
-
| timestamp | 8 | Unix timestamp (ms) |
|
|
438
|
-
| payloadLength | 2 | Payload size |
|
|
439
|
-
| fragmentIndex | 1 | Fragment index |
|
|
440
|
-
| fragmentTotal | 1 | Total fragments |
|
|
441
|
-
| senderId | 8 | Truncated peer ID |
|
|
442
|
-
| recipientId | 8 | (Optional) Recipient ID |
|
|
406
|
+
| Method | What It Does |
|
|
407
|
+
|--------|--------------|
|
|
408
|
+
| `mesh.getConnectionQuality(id)` | Signal quality for one person |
|
|
409
|
+
| `mesh.getAllConnectionQuality()` | Signal quality for everyone |
|
|
443
410
|
|
|
444
|
-
###
|
|
411
|
+
### Channels (Group Chats)
|
|
445
412
|
|
|
446
|
-
|
|
|
447
|
-
|
|
448
|
-
|
|
|
449
|
-
|
|
|
450
|
-
|
|
|
451
|
-
| Channels | CHANNEL_JOIN, CHANNEL_LEAVE, CHANNEL_MESSAGE |
|
|
452
|
-
| Private | PRIVATE_MESSAGE, PRIVATE_ACK, READ_RECEIPT |
|
|
453
|
-
| Control | HEARTBEAT, PING, PONG |
|
|
454
|
-
| Fragments | FRAGMENT_START, FRAGMENT_CONTINUE, FRAGMENT_END |
|
|
413
|
+
| Method | What It Does |
|
|
414
|
+
|--------|--------------|
|
|
415
|
+
| `mesh.joinChannel('#name')` | Join a group |
|
|
416
|
+
| `mesh.leaveChannel('#name')` | Leave a group |
|
|
417
|
+
| `mesh.getChannels()` | See your groups |
|
|
455
418
|
|
|
456
|
-
|
|
419
|
+
### People
|
|
457
420
|
|
|
458
|
-
|
|
421
|
+
| Method | What It Does |
|
|
422
|
+
|--------|--------------|
|
|
423
|
+
| `mesh.getPeers()` | See everyone nearby |
|
|
424
|
+
| `mesh.getConnectedPeers()` | See who's connected |
|
|
425
|
+
| `mesh.blockPeer(id)` | Block someone |
|
|
426
|
+
| `mesh.unblockPeer(id)` | Unblock someone |
|
|
459
427
|
|
|
460
|
-
|
|
461
|
-
const {
|
|
462
|
-
MeshError,
|
|
463
|
-
CryptoError,
|
|
464
|
-
ConnectionError,
|
|
465
|
-
HandshakeError,
|
|
466
|
-
MessageError,
|
|
467
|
-
FragmentError,
|
|
468
|
-
TrustError,
|
|
469
|
-
RetryError
|
|
470
|
-
} = require('react-native-ble-mesh/errors');
|
|
471
|
-
|
|
472
|
-
mesh.on('error', (error) => {
|
|
473
|
-
if (error instanceof CryptoError) {
|
|
474
|
-
console.error('Crypto error:', error.code, error.message);
|
|
475
|
-
} else if (error instanceof ConnectionError) {
|
|
476
|
-
console.error('Connection error:', error.code, error.message);
|
|
477
|
-
} else if (error instanceof TrustError) {
|
|
478
|
-
console.error('Trust error:', error.code, error.message);
|
|
479
|
-
}
|
|
428
|
+
### Safety
|
|
480
429
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
console.log('Details:', error.details);
|
|
486
|
-
});
|
|
487
|
-
```
|
|
430
|
+
| Method | What It Does |
|
|
431
|
+
|--------|--------------|
|
|
432
|
+
| `mesh.enablePanicMode()` | Enable emergency wipe |
|
|
433
|
+
| `mesh.wipeAllData()` | Delete everything NOW |
|
|
488
434
|
|
|
489
|
-
###
|
|
435
|
+
### Network Info
|
|
490
436
|
|
|
491
|
-
|
|
|
492
|
-
|
|
493
|
-
|
|
|
494
|
-
|
|
|
495
|
-
|
|
|
496
|
-
|
|
|
497
|
-
| Fragment | E_FRAG_001 - E_FRAG_004 |
|
|
498
|
-
| Trust | E_TRUST_001 - E_TRUST_004 |
|
|
499
|
-
| Retry | E_RETRY_001 - E_RETRY_003 |
|
|
437
|
+
| Method | What It Does |
|
|
438
|
+
|--------|--------------|
|
|
439
|
+
| `mesh.getStatus()` | Current status |
|
|
440
|
+
| `mesh.getNetworkHealth()` | How good is the network? |
|
|
441
|
+
| `mesh.getBatteryMode()` | Current battery mode |
|
|
442
|
+
| `mesh.setBatteryMode('low')` | Change battery mode |
|
|
500
443
|
|
|
501
|
-
|
|
444
|
+
---
|
|
502
445
|
|
|
503
|
-
|
|
446
|
+
## Events (When Things Happen)
|
|
504
447
|
|
|
505
448
|
```javascript
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
449
|
+
// Messages
|
|
450
|
+
mesh.on('messageReceived', ({ from, text, type }) => { });
|
|
451
|
+
mesh.on('directMessage', ({ from, text }) => { });
|
|
452
|
+
mesh.on('channelMessage', ({ channel, from, text }) => { });
|
|
453
|
+
mesh.on('messageDelivered', ({ messageId }) => { });
|
|
454
|
+
|
|
455
|
+
// Files
|
|
456
|
+
mesh.on('fileReceived', ({ from, file }) => { });
|
|
457
|
+
mesh.on('fileSendProgress', ({ name, percent }) => { });
|
|
458
|
+
mesh.on('fileReceiveProgress', ({ name, percent }) => { });
|
|
459
|
+
mesh.on('fileTransferFailed', ({ transferId, reason }) => { });
|
|
460
|
+
|
|
461
|
+
// People
|
|
462
|
+
mesh.on('peerDiscovered', (peer) => { });
|
|
463
|
+
mesh.on('peerConnected', (peer) => { });
|
|
464
|
+
mesh.on('peerDisconnected', (peer) => { });
|
|
465
|
+
|
|
466
|
+
// Connection Quality
|
|
467
|
+
mesh.on('connectionQualityChanged', ({ peerId, level, score }) => { });
|
|
468
|
+
|
|
469
|
+
// Network
|
|
470
|
+
mesh.on('started', () => { });
|
|
471
|
+
mesh.on('stopped', () => { });
|
|
472
|
+
mesh.on('networkHealthChanged', (info) => { });
|
|
473
|
+
mesh.on('error', (error) => { });
|
|
474
|
+
|
|
475
|
+
// Offline delivery
|
|
476
|
+
mesh.on('messageCached', ({ peerId, text }) => { });
|
|
477
|
+
mesh.on('cachedMessagesDelivered', ({ peerId, delivered }) => { });
|
|
478
|
+
|
|
479
|
+
// Safety
|
|
480
|
+
mesh.on('dataWiped', (result) => { });
|
|
512
481
|
```
|
|
513
482
|
|
|
514
|
-
|
|
515
|
-
- NSBluetoothAlwaysUsageDescription
|
|
516
|
-
- NSBluetoothPeripheralUsageDescription
|
|
483
|
+
---
|
|
517
484
|
|
|
518
|
-
|
|
519
|
-
- BLUETOOTH
|
|
520
|
-
- BLUETOOTH_ADMIN
|
|
521
|
-
- BLUETOOTH_SCAN
|
|
522
|
-
- BLUETOOTH_ADVERTISE
|
|
523
|
-
- BLUETOOTH_CONNECT
|
|
524
|
-
- ACCESS_FINE_LOCATION
|
|
485
|
+
## How Secure Is It?
|
|
525
486
|
|
|
526
|
-
|
|
487
|
+
**Very secure!** Here's what protects your messages:
|
|
527
488
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
489
|
+
| Feature | What It Means (in Simple Words) |
|
|
490
|
+
|---------|-------------------------------|
|
|
491
|
+
| **Pluggable Encryption** | Choose the strongest lock for your messages |
|
|
492
|
+
| **Key Exchange** | Phones secretly agree on a password that nobody else knows |
|
|
493
|
+
| **Forward Secrecy** | Even if someone steals your keys later, old messages stay secret |
|
|
494
|
+
| **No Permanent IDs** | You can't be tracked — you're just "a phone" |
|
|
495
|
+
| **Panic Wipe** | One tap and everything disappears forever |
|
|
531
496
|
|
|
532
|
-
|
|
533
|
-
adapter: new NodeBLEAdapter(),
|
|
534
|
-
});
|
|
535
|
-
```
|
|
497
|
+
---
|
|
536
498
|
|
|
537
|
-
##
|
|
499
|
+
## iOS Background Mode
|
|
538
500
|
|
|
539
|
-
|
|
501
|
+
Want the mesh to keep working when the app is in the background? We've got you covered.
|
|
540
502
|
|
|
541
|
-
|
|
542
|
-
const { MeshService, BLETransport, MemoryStorage } = require('react-native-ble-mesh');
|
|
543
|
-
|
|
544
|
-
class ChatApp {
|
|
545
|
-
constructor() {
|
|
546
|
-
this.mesh = new MeshService();
|
|
547
|
-
this.messages = [];
|
|
548
|
-
}
|
|
503
|
+
**Short version:** Add `bluetooth-central` and `bluetooth-peripheral` to your background modes (the Expo plugin does this automatically).
|
|
549
504
|
|
|
550
|
-
|
|
551
|
-
await this.mesh.initialize({
|
|
552
|
-
displayName,
|
|
553
|
-
storage: new MemoryStorage(),
|
|
554
|
-
});
|
|
505
|
+
**Detailed version:** See our [iOS Background BLE Guide](docs/IOS-BACKGROUND-BLE.md) — covers all the limitations, workarounds, and known bugs.
|
|
555
506
|
|
|
556
|
-
|
|
557
|
-
await this.mesh.start(transport);
|
|
507
|
+
---
|
|
558
508
|
|
|
559
|
-
|
|
560
|
-
console.log('Chat started! Your fingerprint:', this.mesh.getFingerprint());
|
|
561
|
-
}
|
|
509
|
+
## Frequently Asked Questions
|
|
562
510
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
console.log(`[+] Discovered: ${peer.getDisplayName()}`);
|
|
566
|
-
});
|
|
511
|
+
**How far can messages travel?**
|
|
512
|
+
One hop: ~30 meters (100 feet). With 7 hops through other phones: 300+ meters!
|
|
567
513
|
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
console.log(`[${message.senderId.slice(0, 8)}]: ${message.content}`);
|
|
571
|
-
});
|
|
514
|
+
**Does it work if Bluetooth is off?**
|
|
515
|
+
No, Bluetooth must be on. But you don't need WiFi or cell service!
|
|
572
516
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
});
|
|
576
|
-
}
|
|
517
|
+
**Can someone read my private messages?**
|
|
518
|
+
Nope! They're encrypted. Only you and your friend have the keys.
|
|
577
519
|
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
requiresAck: true,
|
|
581
|
-
});
|
|
582
|
-
}
|
|
520
|
+
**How big of a file can I send?**
|
|
521
|
+
Up to 10MB by default (configurable). Big files automatically use Wi-Fi Direct if available.
|
|
583
522
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
}
|
|
523
|
+
**Does it work with Expo?**
|
|
524
|
+
Yes! Just add the plugin to `app.json` and build a dev client.
|
|
587
525
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
}
|
|
526
|
+
**Does it drain my battery?**
|
|
527
|
+
It uses some battery (Bluetooth is on), but use `batteryMode: 'auto'` and the library manages it for you.
|
|
591
528
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
await this.mesh.destroy();
|
|
595
|
-
}
|
|
596
|
-
}
|
|
529
|
+
**Does it work in the background?**
|
|
530
|
+
On iOS, it works with some limitations (scanning is slower). On Android, it works fully. See [iOS Background BLE Guide](docs/IOS-BACKGROUND-BLE.md).
|
|
597
531
|
|
|
598
|
-
|
|
599
|
-
const chat = new ChatApp();
|
|
600
|
-
await chat.start('Alice');
|
|
532
|
+
---
|
|
601
533
|
|
|
602
|
-
|
|
603
|
-
const peers = chat.getPeers();
|
|
604
|
-
peers.forEach(p => console.log(p.id, p.getDisplayName()));
|
|
534
|
+
## Use Cases
|
|
605
535
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
536
|
+
- 🎵 **Concerts & Festivals** — Text friends when cell towers are overloaded
|
|
537
|
+
- ⛺ **Camping & Hiking** — Stay connected in the wilderness
|
|
538
|
+
- 🆘 **Emergencies** — Communicate during power outages or disasters
|
|
539
|
+
- ✊ **Protests & Events** — When networks are restricted
|
|
540
|
+
- 🎮 **Gaming** — Local multiplayer without internet
|
|
541
|
+
- 🏫 **Schools** — Classroom activities without WiFi
|
|
542
|
+
- 📸 **Photo Sharing** — Share pictures at events without data
|
|
609
543
|
|
|
610
|
-
|
|
544
|
+
---
|
|
611
545
|
|
|
612
|
-
|
|
613
|
-
// Messages automatically route through multiple hops
|
|
614
|
-
const messageId = await mesh.sendPrivateMessage(distantPeerId, 'Hello distant peer!', {
|
|
615
|
-
maxHops: 10, // Allow up to 10 hops
|
|
616
|
-
requiresAck: true,
|
|
617
|
-
});
|
|
546
|
+
## Project Structure
|
|
618
547
|
|
|
619
|
-
// Track routing statistics
|
|
620
|
-
const stats = mesh.getStats();
|
|
621
|
-
console.log('Messages relayed:', stats.messagesRelayed);
|
|
622
|
-
console.log('Average hop count:', stats.averageHopCount);
|
|
623
548
|
```
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
// - Fragmentation for BLE MTU limits
|
|
638
|
-
// - Encryption with peer's session key
|
|
639
|
-
// - Automatic retry on failure
|
|
640
|
-
|
|
641
|
-
const messageId = await mesh.sendPrivateMessage(peerId, {
|
|
642
|
-
type: 'file',
|
|
643
|
-
name: fileName,
|
|
644
|
-
data: fileData.toString('base64'),
|
|
645
|
-
size: fileData.length,
|
|
646
|
-
}, {
|
|
647
|
-
compress: true,
|
|
648
|
-
requiresAck: true,
|
|
649
|
-
});
|
|
650
|
-
|
|
651
|
-
console.log('File transfer initiated:', messageId);
|
|
652
|
-
}
|
|
549
|
+
react-native-ble-mesh/
|
|
550
|
+
├── src/
|
|
551
|
+
│ ├── index.js # Main entry point
|
|
552
|
+
│ ├── MeshNetwork.js # High-level API (start here!)
|
|
553
|
+
│ ├── crypto/ # Pluggable encryption providers
|
|
554
|
+
│ ├── mesh/ # Routing, dedup, connection quality
|
|
555
|
+
│ ├── transport/ # BLE + Wi-Fi Direct + MultiTransport
|
|
556
|
+
│ ├── service/ # Messaging, files, audio, battery, panic
|
|
557
|
+
│ ├── expo/ # Expo config plugin
|
|
558
|
+
│ └── hooks/ # React hooks
|
|
559
|
+
├── docs/ # Guides & specs
|
|
560
|
+
├── app.plugin.js # Expo plugin entry
|
|
561
|
+
└── __tests__/ # 432 tests, 0 failures ✅
|
|
653
562
|
```
|
|
654
563
|
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
Run the test suite:
|
|
564
|
+
---
|
|
658
565
|
|
|
659
|
-
|
|
660
|
-
npm test
|
|
661
|
-
```
|
|
566
|
+
## More Documentation
|
|
662
567
|
|
|
663
|
-
|
|
568
|
+
- [Full API Reference](docs/API.md) — Every method explained
|
|
569
|
+
- [React Native Guide](docs/REACT_NATIVE.md) — Platform-specific setup
|
|
570
|
+
- [iOS Background BLE](docs/IOS-BACKGROUND-BLE.md) — Background mode guide
|
|
571
|
+
- [Security Details](docs/SECURITY.md) — How encryption works
|
|
572
|
+
- [Protocol Spec](docs/PROTOCOL.md) — Technical wire format
|
|
573
|
+
- [Optimization Notes](docs/OPTIMIZATION.md) — Performance details
|
|
574
|
+
- [v2.1 Feature Spec](docs/SPEC-v2.1.md) — Architecture decisions
|
|
575
|
+
- [AI/Agent Instructions](docs/prompt-instructions.md) — For AI assistants
|
|
664
576
|
|
|
665
|
-
|
|
666
|
-
npm run test:coverage
|
|
667
|
-
```
|
|
577
|
+
---
|
|
668
578
|
|
|
669
|
-
|
|
579
|
+
## Testing
|
|
670
580
|
|
|
671
581
|
```bash
|
|
672
|
-
npm test
|
|
673
|
-
npm test
|
|
582
|
+
npm test # Run all 432 tests
|
|
583
|
+
npm run test:coverage # With coverage report
|
|
674
584
|
```
|
|
675
585
|
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
```
|
|
679
|
-
react-native-ble-mesh/
|
|
680
|
-
├── src/
|
|
681
|
-
│ ├── index.js # Main exports
|
|
682
|
-
│ ├── constants/ # Protocol, BLE, crypto constants
|
|
683
|
-
│ ├── errors/ # Custom error classes
|
|
684
|
-
│ ├── crypto/ # Cryptographic implementations
|
|
685
|
-
│ │ ├── noise/ # Noise Protocol (handshake, session)
|
|
686
|
-
│ │ └── keys/ # Key management
|
|
687
|
-
│ ├── protocol/ # Message serialization/deserialization
|
|
688
|
-
│ ├── mesh/ # Mesh networking logic
|
|
689
|
-
│ │ ├── router/ # Message routing
|
|
690
|
-
│ │ ├── dedup/ # Duplicate detection
|
|
691
|
-
│ │ ├── fragment/ # Message fragmentation
|
|
692
|
-
│ │ └── peer/ # Peer management
|
|
693
|
-
│ ├── transport/ # BLE transport layer
|
|
694
|
-
│ ├── storage/ # Persistence adapters
|
|
695
|
-
│ ├── utils/ # Utility functions
|
|
696
|
-
│ └── service/ # High-level service orchestration
|
|
697
|
-
├── __tests__/ # Test files
|
|
698
|
-
├── docs/ # Documentation
|
|
699
|
-
├── examples/ # Example applications
|
|
700
|
-
└── package.json
|
|
701
|
-
```
|
|
586
|
+
---
|
|
702
587
|
|
|
703
588
|
## Contributing
|
|
704
589
|
|
|
705
|
-
|
|
706
|
-
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
707
|
-
3. Run tests and linting (`npm run validate`)
|
|
708
|
-
4. Commit your changes (`git commit -m 'Add amazing feature'`)
|
|
709
|
-
5. Push to the branch (`git push origin feature/amazing-feature`)
|
|
710
|
-
6. Open a Pull Request
|
|
590
|
+
We love contributions! Here's how:
|
|
711
591
|
|
|
712
|
-
|
|
592
|
+
1. Fork this repository
|
|
593
|
+
2. Create a branch: `git checkout -b my-feature`
|
|
594
|
+
3. Make your changes
|
|
595
|
+
4. Run tests: `npm test`
|
|
596
|
+
5. Push and create a Pull Request
|
|
713
597
|
|
|
714
|
-
|
|
715
|
-
- JSDoc comments on all public APIs
|
|
716
|
-
- ESLint with strict rules
|
|
717
|
-
- 100% test coverage on crypto modules
|
|
718
|
-
- >80% coverage on other modules
|
|
598
|
+
---
|
|
719
599
|
|
|
720
|
-
##
|
|
600
|
+
## Credits
|
|
721
601
|
|
|
722
|
-
|
|
723
|
-
- [Architecture](docs/ARCHITECTURE.md) - System design and module structure
|
|
724
|
-
- [Security](docs/SECURITY.md) - Threat model and security properties
|
|
725
|
-
- [Protocol](docs/PROTOCOL.md) - Wire format and message types
|
|
602
|
+
Inspired by [BitChat](https://github.com/nicegram/nicegram-bitchat) — the original decentralized mesh chat.
|
|
726
603
|
|
|
727
|
-
|
|
604
|
+
Built with:
|
|
605
|
+
- [react-native-ble-plx](https://github.com/Polidea/react-native-ble-plx) — Bluetooth Low Energy
|
|
606
|
+
- [tweetnacl](https://tweetnacl.js.org/) — Encryption
|
|
728
607
|
|
|
729
|
-
|
|
608
|
+
---
|
|
609
|
+
|
|
610
|
+
## License
|
|
730
611
|
|
|
731
|
-
|
|
612
|
+
MIT License — do whatever you want with it! See [LICENSE](LICENSE) for details.
|
|
732
613
|
|
|
733
|
-
|
|
614
|
+
---
|
|
734
615
|
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
616
|
+
<p align="center">
|
|
617
|
+
<b>Made with ❤️ for a more connected (yet private) world</b>
|
|
618
|
+
<br><br>
|
|
619
|
+
If this library helps you, give it a ⭐ on GitHub!
|
|
620
|
+
</p>
|