react-native-ble-mesh 1.0.4 → 1.1.1
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 +328 -561
- package/docs/prompt-instructions.md +528 -0
- package/package.json +2 -2
- package/src/MeshNetwork.js +787 -0
- package/src/index.d.ts +386 -4
- package/src/index.js +56 -3
- package/src/mesh/index.js +5 -1
- package/src/mesh/monitor/NetworkMonitor.js +543 -0
- package/src/mesh/monitor/index.js +14 -0
- package/src/mesh/store/StoreAndForwardManager.js +476 -0
- package/src/mesh/store/index.js +12 -0
- package/src/service/BatteryOptimizer.js +497 -0
- package/src/service/EmergencyManager.js +370 -0
- package/src/service/index.js +10 -0
- package/src/utils/compression.js +456 -0
- package/src/utils/index.js +9 -1
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Battery Optimizer with Adaptive Power Modes
|
|
5
|
+
* @module service/BatteryOptimizer
|
|
6
|
+
*
|
|
7
|
+
* Provides three power profiles (high/balanced/low) with automatic
|
|
8
|
+
* scanning adjustments based on battery level and network activity.
|
|
9
|
+
*
|
|
10
|
+
* Target: <5% battery drain per hour in balanced mode.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const EventEmitter = require('../utils/EventEmitter');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Battery mode constants
|
|
17
|
+
* @constant {Object}
|
|
18
|
+
*/
|
|
19
|
+
const BATTERY_MODE = Object.freeze({
|
|
20
|
+
HIGH_PERFORMANCE: 'high',
|
|
21
|
+
BALANCED: 'balanced',
|
|
22
|
+
LOW_POWER: 'low',
|
|
23
|
+
AUTO: 'auto',
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Battery profile configuration
|
|
28
|
+
* @typedef {Object} BatteryProfile
|
|
29
|
+
* @property {number} scanIntervalMs - BLE scan interval
|
|
30
|
+
* @property {number} scanWindowMs - BLE scan window
|
|
31
|
+
* @property {number} connectionIntervalMs - Connection interval
|
|
32
|
+
* @property {number} advertisingIntervalMs - Advertising interval
|
|
33
|
+
* @property {number} heartbeatIntervalMs - Heartbeat frequency
|
|
34
|
+
* @property {string} description - Human-readable description
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Default battery profiles
|
|
39
|
+
* @constant {Object.<string, BatteryProfile>}
|
|
40
|
+
*/
|
|
41
|
+
const DEFAULT_PROFILES = Object.freeze({
|
|
42
|
+
[BATTERY_MODE.HIGH_PERFORMANCE]: {
|
|
43
|
+
scanIntervalMs: 100,
|
|
44
|
+
scanWindowMs: 50,
|
|
45
|
+
connectionIntervalMs: 7.5,
|
|
46
|
+
advertisingIntervalMs: 100,
|
|
47
|
+
heartbeatIntervalMs: 10000,
|
|
48
|
+
description: 'Maximum performance, high battery usage',
|
|
49
|
+
},
|
|
50
|
+
[BATTERY_MODE.BALANCED]: {
|
|
51
|
+
scanIntervalMs: 500,
|
|
52
|
+
scanWindowMs: 100,
|
|
53
|
+
connectionIntervalMs: 30,
|
|
54
|
+
advertisingIntervalMs: 500,
|
|
55
|
+
heartbeatIntervalMs: 30000,
|
|
56
|
+
description: 'Balanced performance and battery, <5% drain/hour',
|
|
57
|
+
},
|
|
58
|
+
[BATTERY_MODE.LOW_POWER]: {
|
|
59
|
+
scanIntervalMs: 2000,
|
|
60
|
+
scanWindowMs: 200,
|
|
61
|
+
connectionIntervalMs: 100,
|
|
62
|
+
advertisingIntervalMs: 2000,
|
|
63
|
+
heartbeatIntervalMs: 60000,
|
|
64
|
+
description: 'Minimum battery usage, reduced responsiveness',
|
|
65
|
+
},
|
|
66
|
+
[BATTERY_MODE.AUTO]: {
|
|
67
|
+
// Auto mode uses balanced as base, adjusts dynamically
|
|
68
|
+
scanIntervalMs: 500,
|
|
69
|
+
scanWindowMs: 100,
|
|
70
|
+
connectionIntervalMs: 30,
|
|
71
|
+
advertisingIntervalMs: 500,
|
|
72
|
+
heartbeatIntervalMs: 30000,
|
|
73
|
+
description: 'Automatic adjustment based on battery level',
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Battery level thresholds for auto mode
|
|
79
|
+
* @constant {Object}
|
|
80
|
+
*/
|
|
81
|
+
const BATTERY_THRESHOLDS = Object.freeze({
|
|
82
|
+
HIGH: 50, // Above 50%: high performance
|
|
83
|
+
MEDIUM: 20, // 20-50%: balanced
|
|
84
|
+
LOW: 10, // 10-20%: low power
|
|
85
|
+
CRITICAL: 5, // Below 5%: ultra low power
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Default configuration
|
|
90
|
+
* @constant {Object}
|
|
91
|
+
*/
|
|
92
|
+
const DEFAULT_CONFIG = Object.freeze({
|
|
93
|
+
/** Initial battery mode */
|
|
94
|
+
initialMode: BATTERY_MODE.BALANCED,
|
|
95
|
+
/** Enable automatic mode switching */
|
|
96
|
+
autoAdjust: true,
|
|
97
|
+
/** Battery check interval (ms) */
|
|
98
|
+
batteryCheckIntervalMs: 60000,
|
|
99
|
+
/** Activity-based adjustment enabled */
|
|
100
|
+
activityAdjust: true,
|
|
101
|
+
/** Inactivity timeout for power reduction (ms) */
|
|
102
|
+
inactivityTimeoutMs: 5 * 60 * 1000,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Battery Optimizer for adaptive power management.
|
|
107
|
+
*
|
|
108
|
+
* @class BatteryOptimizer
|
|
109
|
+
* @extends EventEmitter
|
|
110
|
+
* @example
|
|
111
|
+
* const optimizer = new BatteryOptimizer();
|
|
112
|
+
*
|
|
113
|
+
* // Set mode manually
|
|
114
|
+
* await optimizer.setMode(BATTERY_MODE.BALANCED);
|
|
115
|
+
*
|
|
116
|
+
* // Enable auto-adjustment
|
|
117
|
+
* optimizer.setAutoAdjust(true);
|
|
118
|
+
*
|
|
119
|
+
* // Report battery level
|
|
120
|
+
* optimizer.updateBatteryLevel(75);
|
|
121
|
+
*
|
|
122
|
+
* // Get current profile
|
|
123
|
+
* const profile = optimizer.getCurrentProfile();
|
|
124
|
+
*/
|
|
125
|
+
class BatteryOptimizer extends EventEmitter {
|
|
126
|
+
/**
|
|
127
|
+
* Creates a new BatteryOptimizer instance.
|
|
128
|
+
* @param {Object} [options={}] - Configuration options
|
|
129
|
+
*/
|
|
130
|
+
constructor(options = {}) {
|
|
131
|
+
super();
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Configuration
|
|
135
|
+
* @type {Object}
|
|
136
|
+
* @private
|
|
137
|
+
*/
|
|
138
|
+
this._config = { ...DEFAULT_CONFIG, ...options };
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Battery profiles
|
|
142
|
+
* @type {Object.<string, BatteryProfile>}
|
|
143
|
+
* @private
|
|
144
|
+
*/
|
|
145
|
+
this._profiles = { ...DEFAULT_PROFILES };
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Current battery mode
|
|
149
|
+
* @type {string}
|
|
150
|
+
* @private
|
|
151
|
+
*/
|
|
152
|
+
this._currentMode = this._config.initialMode;
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Current battery level (0-100)
|
|
156
|
+
* @type {number}
|
|
157
|
+
* @private
|
|
158
|
+
*/
|
|
159
|
+
this._batteryLevel = 100;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Is charging
|
|
163
|
+
* @type {boolean}
|
|
164
|
+
* @private
|
|
165
|
+
*/
|
|
166
|
+
this._isCharging = false;
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Last activity timestamp
|
|
170
|
+
* @type {number}
|
|
171
|
+
* @private
|
|
172
|
+
*/
|
|
173
|
+
this._lastActivityTime = Date.now();
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Auto adjustment enabled
|
|
177
|
+
* @type {boolean}
|
|
178
|
+
* @private
|
|
179
|
+
*/
|
|
180
|
+
this._autoAdjust = this._config.autoAdjust;
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Battery check timer
|
|
184
|
+
* @type {number|null}
|
|
185
|
+
* @private
|
|
186
|
+
*/
|
|
187
|
+
this._batteryCheckTimer = null;
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Transport reference for applying settings
|
|
191
|
+
* @type {Object|null}
|
|
192
|
+
* @private
|
|
193
|
+
*/
|
|
194
|
+
this._transport = null;
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Statistics
|
|
198
|
+
* @type {Object}
|
|
199
|
+
* @private
|
|
200
|
+
*/
|
|
201
|
+
this._stats = {
|
|
202
|
+
modeChanges: 0,
|
|
203
|
+
autoAdjustments: 0,
|
|
204
|
+
lastModeChange: null,
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
// Start battery monitoring if auto-adjust enabled
|
|
208
|
+
if (this._autoAdjust) {
|
|
209
|
+
this._startBatteryMonitoring();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Sets the transport to control.
|
|
215
|
+
* @param {Object} transport - Transport instance
|
|
216
|
+
*/
|
|
217
|
+
setTransport(transport) {
|
|
218
|
+
this._transport = transport;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Sets the battery mode.
|
|
223
|
+
* @param {string} mode - Battery mode
|
|
224
|
+
* @returns {Promise<void>}
|
|
225
|
+
*/
|
|
226
|
+
async setMode(mode) {
|
|
227
|
+
if (!Object.values(BATTERY_MODE).includes(mode)) {
|
|
228
|
+
throw new Error(`Invalid battery mode: ${mode}`);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const previousMode = this._currentMode;
|
|
232
|
+
this._currentMode = mode;
|
|
233
|
+
|
|
234
|
+
// If switching to AUTO, determine actual profile from battery level
|
|
235
|
+
const activeProfile = mode === BATTERY_MODE.AUTO
|
|
236
|
+
? this._getProfileForBatteryLevel(this._batteryLevel)
|
|
237
|
+
: this._profiles[mode];
|
|
238
|
+
|
|
239
|
+
// Apply to transport
|
|
240
|
+
await this._applyProfile(activeProfile);
|
|
241
|
+
|
|
242
|
+
this._stats.modeChanges++;
|
|
243
|
+
this._stats.lastModeChange = Date.now();
|
|
244
|
+
|
|
245
|
+
this.emit('mode-changed', {
|
|
246
|
+
previous: previousMode,
|
|
247
|
+
current: mode,
|
|
248
|
+
profile: activeProfile,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Gets the current battery mode.
|
|
254
|
+
* @returns {string} Current mode
|
|
255
|
+
*/
|
|
256
|
+
getMode() {
|
|
257
|
+
return this._currentMode;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Gets the current active profile.
|
|
262
|
+
* @returns {BatteryProfile} Active profile
|
|
263
|
+
*/
|
|
264
|
+
getCurrentProfile() {
|
|
265
|
+
if (this._currentMode === BATTERY_MODE.AUTO) {
|
|
266
|
+
return this._getProfileForBatteryLevel(this._batteryLevel);
|
|
267
|
+
}
|
|
268
|
+
return this._profiles[this._currentMode];
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Gets all available profiles.
|
|
273
|
+
* @returns {Object.<string, BatteryProfile>} Profiles
|
|
274
|
+
*/
|
|
275
|
+
getProfiles() {
|
|
276
|
+
return { ...this._profiles };
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Updates the battery level and triggers auto-adjustment if enabled.
|
|
281
|
+
* @param {number} level - Battery level (0-100)
|
|
282
|
+
* @param {boolean} [isCharging=false] - Whether device is charging
|
|
283
|
+
*/
|
|
284
|
+
async updateBatteryLevel(level, isCharging = false) {
|
|
285
|
+
const previousLevel = this._batteryLevel;
|
|
286
|
+
this._batteryLevel = Math.max(0, Math.min(100, level));
|
|
287
|
+
this._isCharging = isCharging;
|
|
288
|
+
|
|
289
|
+
this.emit('battery-updated', {
|
|
290
|
+
level: this._batteryLevel,
|
|
291
|
+
isCharging,
|
|
292
|
+
previousLevel,
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
// Auto-adjust if enabled and in AUTO mode
|
|
296
|
+
if (this._autoAdjust && this._currentMode === BATTERY_MODE.AUTO) {
|
|
297
|
+
await this._autoAdjustMode();
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Enables or disables auto-adjustment.
|
|
303
|
+
* @param {boolean} enabled - Whether to enable
|
|
304
|
+
*/
|
|
305
|
+
setAutoAdjust(enabled) {
|
|
306
|
+
this._autoAdjust = enabled;
|
|
307
|
+
|
|
308
|
+
if (enabled && !this._batteryCheckTimer) {
|
|
309
|
+
this._startBatteryMonitoring();
|
|
310
|
+
} else if (!enabled && this._batteryCheckTimer) {
|
|
311
|
+
this._stopBatteryMonitoring();
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
this.emit('auto-adjust-changed', { enabled });
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Checks if auto-adjustment is enabled.
|
|
319
|
+
* @returns {boolean} True if enabled
|
|
320
|
+
*/
|
|
321
|
+
isAutoAdjustEnabled() {
|
|
322
|
+
return this._autoAdjust;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Records user activity (for activity-based optimization).
|
|
327
|
+
*/
|
|
328
|
+
recordActivity() {
|
|
329
|
+
this._lastActivityTime = Date.now();
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Gets the battery level.
|
|
334
|
+
* @returns {number} Battery level (0-100)
|
|
335
|
+
*/
|
|
336
|
+
getBatteryLevel() {
|
|
337
|
+
return this._batteryLevel;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Checks if device is charging.
|
|
342
|
+
* @returns {boolean} True if charging
|
|
343
|
+
*/
|
|
344
|
+
isCharging() {
|
|
345
|
+
return this._isCharging;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Gets optimizer statistics.
|
|
350
|
+
* @returns {Object} Statistics
|
|
351
|
+
*/
|
|
352
|
+
getStats() {
|
|
353
|
+
return {
|
|
354
|
+
...this._stats,
|
|
355
|
+
currentMode: this._currentMode,
|
|
356
|
+
batteryLevel: this._batteryLevel,
|
|
357
|
+
isCharging: this._isCharging,
|
|
358
|
+
autoAdjust: this._autoAdjust,
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Destroys the optimizer.
|
|
364
|
+
*/
|
|
365
|
+
destroy() {
|
|
366
|
+
this._stopBatteryMonitoring();
|
|
367
|
+
this._transport = null;
|
|
368
|
+
this.removeAllListeners();
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Gets the appropriate profile for a battery level.
|
|
373
|
+
* @param {number} level - Battery level
|
|
374
|
+
* @returns {BatteryProfile} Profile
|
|
375
|
+
* @private
|
|
376
|
+
*/
|
|
377
|
+
_getProfileForBatteryLevel(level) {
|
|
378
|
+
if (this._isCharging) {
|
|
379
|
+
return this._profiles[BATTERY_MODE.HIGH_PERFORMANCE];
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (level > BATTERY_THRESHOLDS.HIGH) {
|
|
383
|
+
return this._profiles[BATTERY_MODE.HIGH_PERFORMANCE];
|
|
384
|
+
} else if (level > BATTERY_THRESHOLDS.MEDIUM) {
|
|
385
|
+
return this._profiles[BATTERY_MODE.BALANCED];
|
|
386
|
+
} else {
|
|
387
|
+
return this._profiles[BATTERY_MODE.LOW_POWER];
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Auto-adjusts mode based on battery level.
|
|
393
|
+
* @returns {Promise<void>}
|
|
394
|
+
* @private
|
|
395
|
+
*/
|
|
396
|
+
async _autoAdjustMode() {
|
|
397
|
+
const profile = this._getProfileForBatteryLevel(this._batteryLevel);
|
|
398
|
+
const currentProfile = this.getCurrentProfile();
|
|
399
|
+
|
|
400
|
+
// Only apply if profile actually changed
|
|
401
|
+
if (profile.scanIntervalMs !== currentProfile.scanIntervalMs) {
|
|
402
|
+
await this._applyProfile(profile);
|
|
403
|
+
this._stats.autoAdjustments++;
|
|
404
|
+
|
|
405
|
+
this.emit('auto-adjusted', {
|
|
406
|
+
batteryLevel: this._batteryLevel,
|
|
407
|
+
profile,
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Applies a battery profile to the transport.
|
|
414
|
+
* @param {BatteryProfile} profile - Profile to apply
|
|
415
|
+
* @returns {Promise<void>}
|
|
416
|
+
* @private
|
|
417
|
+
*/
|
|
418
|
+
async _applyProfile(profile) {
|
|
419
|
+
if (!this._transport) {
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
try {
|
|
424
|
+
// Apply scan parameters
|
|
425
|
+
if (typeof this._transport.setScanParameters === 'function') {
|
|
426
|
+
await this._transport.setScanParameters({
|
|
427
|
+
interval: profile.scanIntervalMs,
|
|
428
|
+
window: profile.scanWindowMs,
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Apply connection parameters
|
|
433
|
+
if (typeof this._transport.setConnectionParameters === 'function') {
|
|
434
|
+
await this._transport.setConnectionParameters({
|
|
435
|
+
interval: profile.connectionIntervalMs,
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Apply advertising parameters
|
|
440
|
+
if (typeof this._transport.setAdvertisingInterval === 'function') {
|
|
441
|
+
await this._transport.setAdvertisingInterval(profile.advertisingIntervalMs);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
this.emit('profile-applied', { profile });
|
|
445
|
+
} catch (error) {
|
|
446
|
+
this.emit('error', {
|
|
447
|
+
message: 'Failed to apply battery profile',
|
|
448
|
+
error: error.message,
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Starts battery monitoring timer.
|
|
455
|
+
* @private
|
|
456
|
+
*/
|
|
457
|
+
_startBatteryMonitoring() {
|
|
458
|
+
if (this._batteryCheckTimer) {
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
this._batteryCheckTimer = setInterval(
|
|
463
|
+
async () => {
|
|
464
|
+
// Check for inactivity
|
|
465
|
+
if (this._config.activityAdjust) {
|
|
466
|
+
const inactiveTime = Date.now() - this._lastActivityTime;
|
|
467
|
+
if (inactiveTime > this._config.inactivityTimeoutMs) {
|
|
468
|
+
// Switch to low power if inactive
|
|
469
|
+
if (this._currentMode === BATTERY_MODE.AUTO) {
|
|
470
|
+
await this._applyProfile(this._profiles[BATTERY_MODE.LOW_POWER]);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
},
|
|
475
|
+
this._config.batteryCheckIntervalMs
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Stops battery monitoring timer.
|
|
481
|
+
* @private
|
|
482
|
+
*/
|
|
483
|
+
_stopBatteryMonitoring() {
|
|
484
|
+
if (this._batteryCheckTimer) {
|
|
485
|
+
clearInterval(this._batteryCheckTimer);
|
|
486
|
+
this._batteryCheckTimer = null;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
module.exports = {
|
|
492
|
+
BatteryOptimizer,
|
|
493
|
+
BATTERY_MODE,
|
|
494
|
+
BATTERY_THRESHOLDS,
|
|
495
|
+
DEFAULT_PROFILES,
|
|
496
|
+
DEFAULT_CONFIG,
|
|
497
|
+
};
|