react-native-ble-mesh 1.1.1 → 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.
Files changed (65) hide show
  1. package/README.md +288 -172
  2. package/docs/IOS-BACKGROUND-BLE.md +231 -0
  3. package/docs/OPTIMIZATION.md +70 -0
  4. package/docs/SPEC-v2.1.md +308 -0
  5. package/package.json +1 -1
  6. package/src/MeshNetwork.js +659 -465
  7. package/src/constants/index.js +1 -0
  8. package/src/crypto/AutoCrypto.js +79 -0
  9. package/src/crypto/CryptoProvider.js +99 -0
  10. package/src/crypto/index.js +15 -63
  11. package/src/crypto/providers/ExpoCryptoProvider.js +125 -0
  12. package/src/crypto/providers/QuickCryptoProvider.js +134 -0
  13. package/src/crypto/providers/TweetNaClProvider.js +124 -0
  14. package/src/crypto/providers/index.js +11 -0
  15. package/src/errors/MeshError.js +2 -1
  16. package/src/expo/withBLEMesh.js +102 -0
  17. package/src/hooks/useMesh.js +30 -9
  18. package/src/hooks/useMessages.js +2 -0
  19. package/src/index.js +23 -8
  20. package/src/mesh/dedup/DedupManager.js +36 -10
  21. package/src/mesh/fragment/Assembler.js +5 -0
  22. package/src/mesh/index.js +1 -1
  23. package/src/mesh/monitor/ConnectionQuality.js +408 -0
  24. package/src/mesh/monitor/NetworkMonitor.js +327 -316
  25. package/src/mesh/monitor/index.js +7 -3
  26. package/src/mesh/peer/PeerManager.js +6 -1
  27. package/src/mesh/router/MessageRouter.js +26 -15
  28. package/src/mesh/router/RouteTable.js +7 -1
  29. package/src/mesh/store/StoreAndForwardManager.js +295 -297
  30. package/src/mesh/store/index.js +1 -1
  31. package/src/service/BatteryOptimizer.js +282 -278
  32. package/src/service/EmergencyManager.js +224 -214
  33. package/src/service/HandshakeManager.js +167 -13
  34. package/src/service/MeshService.js +72 -6
  35. package/src/service/SessionManager.js +77 -2
  36. package/src/service/audio/AudioManager.js +8 -2
  37. package/src/service/file/FileAssembler.js +106 -0
  38. package/src/service/file/FileChunker.js +79 -0
  39. package/src/service/file/FileManager.js +307 -0
  40. package/src/service/file/FileMessage.js +122 -0
  41. package/src/service/file/index.js +15 -0
  42. package/src/service/text/broadcast/BroadcastManager.js +16 -0
  43. package/src/transport/BLETransport.js +131 -9
  44. package/src/transport/MockTransport.js +1 -1
  45. package/src/transport/MultiTransport.js +305 -0
  46. package/src/transport/WiFiDirectTransport.js +295 -0
  47. package/src/transport/adapters/NodeBLEAdapter.js +34 -0
  48. package/src/transport/adapters/RNBLEAdapter.js +56 -1
  49. package/src/transport/index.js +6 -0
  50. package/src/utils/compression.js +291 -291
  51. package/src/crypto/aead.js +0 -189
  52. package/src/crypto/chacha20.js +0 -181
  53. package/src/crypto/hkdf.js +0 -187
  54. package/src/crypto/hmac.js +0 -143
  55. package/src/crypto/keys/KeyManager.js +0 -271
  56. package/src/crypto/keys/KeyPair.js +0 -216
  57. package/src/crypto/keys/SecureStorage.js +0 -219
  58. package/src/crypto/keys/index.js +0 -32
  59. package/src/crypto/noise/handshake.js +0 -410
  60. package/src/crypto/noise/index.js +0 -27
  61. package/src/crypto/noise/session.js +0 -253
  62. package/src/crypto/noise/state.js +0 -268
  63. package/src/crypto/poly1305.js +0 -113
  64. package/src/crypto/sha256.js +0 -240
  65. package/src/crypto/x25519.js +0 -154
@@ -3,10 +3,10 @@
3
3
  /**
4
4
  * @fileoverview Battery Optimizer with Adaptive Power Modes
5
5
  * @module service/BatteryOptimizer
6
- *
6
+ *
7
7
  * Provides three power profiles (high/balanced/low) with automatic
8
8
  * scanning adjustments based on battery level and network activity.
9
- *
9
+ *
10
10
  * Target: <5% battery drain per hour in balanced mode.
11
11
  */
12
12
 
@@ -17,10 +17,10 @@ const EventEmitter = require('../utils/EventEmitter');
17
17
  * @constant {Object}
18
18
  */
19
19
  const BATTERY_MODE = Object.freeze({
20
- HIGH_PERFORMANCE: 'high',
21
- BALANCED: 'balanced',
22
- LOW_POWER: 'low',
23
- AUTO: 'auto',
20
+ HIGH_PERFORMANCE: 'high',
21
+ BALANCED: 'balanced',
22
+ LOW_POWER: 'low',
23
+ AUTO: 'auto'
24
24
  });
25
25
 
26
26
  /**
@@ -39,39 +39,39 @@ const BATTERY_MODE = Object.freeze({
39
39
  * @constant {Object.<string, BatteryProfile>}
40
40
  */
41
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
- },
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
75
  });
76
76
 
77
77
  /**
@@ -79,10 +79,10 @@ const DEFAULT_PROFILES = Object.freeze({
79
79
  * @constant {Object}
80
80
  */
81
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
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
86
  });
87
87
 
88
88
  /**
@@ -90,408 +90,412 @@ const BATTERY_THRESHOLDS = Object.freeze({
90
90
  * @constant {Object}
91
91
  */
92
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,
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
103
  });
104
104
 
105
105
  /**
106
106
  * Battery Optimizer for adaptive power management.
107
- *
107
+ *
108
108
  * @class BatteryOptimizer
109
109
  * @extends EventEmitter
110
110
  * @example
111
111
  * const optimizer = new BatteryOptimizer();
112
- *
112
+ *
113
113
  * // Set mode manually
114
114
  * await optimizer.setMode(BATTERY_MODE.BALANCED);
115
- *
115
+ *
116
116
  * // Enable auto-adjustment
117
117
  * optimizer.setAutoAdjust(true);
118
- *
118
+ *
119
119
  * // Report battery level
120
120
  * optimizer.updateBatteryLevel(75);
121
- *
121
+ *
122
122
  * // Get current profile
123
123
  * const profile = optimizer.getCurrentProfile();
124
124
  */
125
125
  class BatteryOptimizer extends EventEmitter {
126
- /**
126
+ /**
127
127
  * Creates a new BatteryOptimizer instance.
128
128
  * @param {Object} [options={}] - Configuration options
129
129
  */
130
- constructor(options = {}) {
131
- super();
130
+ constructor(options = {}) {
131
+ super();
132
132
 
133
- /**
133
+ /**
134
134
  * Configuration
135
135
  * @type {Object}
136
136
  * @private
137
137
  */
138
- this._config = { ...DEFAULT_CONFIG, ...options };
138
+ this._config = { ...DEFAULT_CONFIG, ...options };
139
139
 
140
- /**
140
+ /**
141
141
  * Battery profiles
142
142
  * @type {Object.<string, BatteryProfile>}
143
143
  * @private
144
144
  */
145
- this._profiles = { ...DEFAULT_PROFILES };
145
+ this._profiles = { ...DEFAULT_PROFILES };
146
146
 
147
- /**
147
+ /**
148
148
  * Current battery mode
149
149
  * @type {string}
150
150
  * @private
151
151
  */
152
- this._currentMode = this._config.initialMode;
152
+ this._currentMode = this._config.initialMode;
153
153
 
154
- /**
154
+ /**
155
155
  * Current battery level (0-100)
156
156
  * @type {number}
157
157
  * @private
158
158
  */
159
- this._batteryLevel = 100;
159
+ this._batteryLevel = 100;
160
160
 
161
- /**
161
+ /**
162
162
  * Is charging
163
163
  * @type {boolean}
164
164
  * @private
165
165
  */
166
- this._isCharging = false;
166
+ this._isCharging = false;
167
167
 
168
- /**
168
+ /**
169
169
  * Last activity timestamp
170
170
  * @type {number}
171
171
  * @private
172
172
  */
173
- this._lastActivityTime = Date.now();
173
+ this._lastActivityTime = Date.now();
174
174
 
175
- /**
175
+ /**
176
176
  * Auto adjustment enabled
177
177
  * @type {boolean}
178
178
  * @private
179
179
  */
180
- this._autoAdjust = this._config.autoAdjust;
180
+ this._autoAdjust = this._config.autoAdjust;
181
181
 
182
- /**
182
+ /**
183
183
  * Battery check timer
184
184
  * @type {number|null}
185
185
  * @private
186
186
  */
187
- this._batteryCheckTimer = null;
187
+ this._batteryCheckTimer = null;
188
188
 
189
- /**
189
+ /**
190
190
  * Transport reference for applying settings
191
191
  * @type {Object|null}
192
192
  * @private
193
193
  */
194
- this._transport = null;
194
+ this._transport = null;
195
195
 
196
- /**
196
+ /**
197
197
  * Statistics
198
198
  * @type {Object}
199
199
  * @private
200
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
- }
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();
211
210
  }
211
+ }
212
212
 
213
- /**
213
+ /**
214
214
  * Sets the transport to control.
215
215
  * @param {Object} transport - Transport instance
216
216
  */
217
- setTransport(transport) {
218
- this._transport = transport;
219
- }
217
+ setTransport(transport) {
218
+ this._transport = transport;
219
+ }
220
220
 
221
- /**
221
+ /**
222
222
  * Sets the battery mode.
223
223
  * @param {string} mode - Battery mode
224
224
  * @returns {Promise<void>}
225
225
  */
226
- async setMode(mode) {
227
- if (!Object.values(BATTERY_MODE).includes(mode)) {
228
- throw new Error(`Invalid battery mode: ${mode}`);
229
- }
226
+ async setMode(mode) {
227
+ if (!Object.values(BATTERY_MODE).includes(mode)) {
228
+ throw new Error(`Invalid battery mode: ${mode}`);
229
+ }
230
230
 
231
- const previousMode = this._currentMode;
232
- this._currentMode = mode;
231
+ const previousMode = this._currentMode;
232
+ this._currentMode = mode;
233
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];
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
238
 
239
- // Apply to transport
240
- await this._applyProfile(activeProfile);
239
+ // Apply to transport
240
+ await this._applyProfile(activeProfile);
241
241
 
242
- this._stats.modeChanges++;
243
- this._stats.lastModeChange = Date.now();
242
+ this._stats.modeChanges++;
243
+ this._stats.lastModeChange = Date.now();
244
244
 
245
- this.emit('mode-changed', {
246
- previous: previousMode,
247
- current: mode,
248
- profile: activeProfile,
249
- });
250
- }
245
+ this.emit('mode-changed', {
246
+ previous: previousMode,
247
+ current: mode,
248
+ profile: activeProfile
249
+ });
250
+ }
251
251
 
252
- /**
252
+ /**
253
253
  * Gets the current battery mode.
254
254
  * @returns {string} Current mode
255
255
  */
256
- getMode() {
257
- return this._currentMode;
258
- }
256
+ getMode() {
257
+ return this._currentMode;
258
+ }
259
259
 
260
- /**
260
+ /**
261
261
  * Gets the current active profile.
262
262
  * @returns {BatteryProfile} Active profile
263
263
  */
264
- getCurrentProfile() {
265
- if (this._currentMode === BATTERY_MODE.AUTO) {
266
- return this._getProfileForBatteryLevel(this._batteryLevel);
267
- }
268
- return this._profiles[this._currentMode];
264
+ getCurrentProfile() {
265
+ if (this._currentMode === BATTERY_MODE.AUTO) {
266
+ return this._getProfileForBatteryLevel(this._batteryLevel);
269
267
  }
268
+ return this._profiles[this._currentMode];
269
+ }
270
270
 
271
- /**
271
+ /**
272
272
  * Gets all available profiles.
273
273
  * @returns {Object.<string, BatteryProfile>} Profiles
274
274
  */
275
- getProfiles() {
276
- return { ...this._profiles };
277
- }
275
+ getProfiles() {
276
+ return { ...this._profiles };
277
+ }
278
278
 
279
- /**
279
+ /**
280
280
  * Updates the battery level and triggers auto-adjustment if enabled.
281
281
  * @param {number} level - Battery level (0-100)
282
282
  * @param {boolean} [isCharging=false] - Whether device is charging
283
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
- }
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();
299
298
  }
299
+ }
300
300
 
301
- /**
301
+ /**
302
302
  * Enables or disables auto-adjustment.
303
303
  * @param {boolean} enabled - Whether to enable
304
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
- }
305
+ setAutoAdjust(enabled) {
306
+ this._autoAdjust = enabled;
313
307
 
314
- this.emit('auto-adjust-changed', { enabled });
308
+ if (enabled && !this._batteryCheckTimer) {
309
+ this._startBatteryMonitoring();
310
+ } else if (!enabled && this._batteryCheckTimer) {
311
+ this._stopBatteryMonitoring();
315
312
  }
316
313
 
317
- /**
314
+ this.emit('auto-adjust-changed', { enabled });
315
+ }
316
+
317
+ /**
318
318
  * Checks if auto-adjustment is enabled.
319
319
  * @returns {boolean} True if enabled
320
320
  */
321
- isAutoAdjustEnabled() {
322
- return this._autoAdjust;
323
- }
321
+ isAutoAdjustEnabled() {
322
+ return this._autoAdjust;
323
+ }
324
324
 
325
- /**
325
+ /**
326
326
  * Records user activity (for activity-based optimization).
327
327
  */
328
- recordActivity() {
329
- this._lastActivityTime = Date.now();
330
- }
328
+ recordActivity() {
329
+ this._lastActivityTime = Date.now();
330
+ }
331
331
 
332
- /**
332
+ /**
333
333
  * Gets the battery level.
334
334
  * @returns {number} Battery level (0-100)
335
335
  */
336
- getBatteryLevel() {
337
- return this._batteryLevel;
338
- }
336
+ getBatteryLevel() {
337
+ return this._batteryLevel;
338
+ }
339
339
 
340
- /**
340
+ /**
341
341
  * Checks if device is charging.
342
342
  * @returns {boolean} True if charging
343
343
  */
344
- isCharging() {
345
- return this._isCharging;
346
- }
344
+ isCharging() {
345
+ return this._isCharging;
346
+ }
347
347
 
348
- /**
348
+ /**
349
349
  * Gets optimizer statistics.
350
350
  * @returns {Object} Statistics
351
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
- /**
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
363
  * Destroys the optimizer.
364
364
  */
365
- destroy() {
366
- this._stopBatteryMonitoring();
367
- this._transport = null;
368
- this.removeAllListeners();
369
- }
365
+ destroy() {
366
+ this._stopBatteryMonitoring();
367
+ this._transport = null;
368
+ this.removeAllListeners();
369
+ }
370
370
 
371
- /**
371
+ /**
372
372
  * Gets the appropriate profile for a battery level.
373
373
  * @param {number} level - Battery level
374
374
  * @returns {BatteryProfile} Profile
375
375
  * @private
376
376
  */
377
- _getProfileForBatteryLevel(level) {
378
- if (this._isCharging) {
379
- return this._profiles[BATTERY_MODE.HIGH_PERFORMANCE];
380
- }
377
+ _getProfileForBatteryLevel(level) {
378
+ if (this._isCharging) {
379
+ return this._profiles[BATTERY_MODE.HIGH_PERFORMANCE];
380
+ }
381
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
- }
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];
389
388
  }
389
+ }
390
390
 
391
- /**
391
+ /**
392
392
  * Auto-adjusts mode based on battery level.
393
393
  * @returns {Promise<void>}
394
394
  * @private
395
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
- }
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
+ });
410
409
  }
410
+ }
411
411
 
412
- /**
412
+ /**
413
413
  * Applies a battery profile to the transport.
414
414
  * @param {BatteryProfile} profile - Profile to apply
415
415
  * @returns {Promise<void>}
416
416
  * @private
417
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
- }
418
+ async _applyProfile(profile) {
419
+ if (!this._transport) {
420
+ return;
421
+ }
438
422
 
439
- // Apply advertising parameters
440
- if (typeof this._transport.setAdvertisingInterval === 'function') {
441
- await this._transport.setAdvertisingInterval(profile.advertisingIntervalMs);
442
- }
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
+ }
443
431
 
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
- }
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
+ });
451
450
  }
451
+ }
452
452
 
453
- /**
453
+ /**
454
454
  * Starts battery monitoring timer.
455
455
  * @private
456
456
  */
457
- _startBatteryMonitoring() {
458
- if (this._batteryCheckTimer) {
459
- return;
460
- }
457
+ _startBatteryMonitoring() {
458
+ if (this._batteryCheckTimer) {
459
+ return;
460
+ }
461
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
- );
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
+ // Don't prevent process exit (important for tests and cleanup)
478
+ if (this._batteryCheckTimer && typeof this._batteryCheckTimer.unref === 'function') {
479
+ this._batteryCheckTimer.unref();
477
480
  }
481
+ }
478
482
 
479
- /**
483
+ /**
480
484
  * Stops battery monitoring timer.
481
485
  * @private
482
486
  */
483
- _stopBatteryMonitoring() {
484
- if (this._batteryCheckTimer) {
485
- clearInterval(this._batteryCheckTimer);
486
- this._batteryCheckTimer = null;
487
- }
487
+ _stopBatteryMonitoring() {
488
+ if (this._batteryCheckTimer) {
489
+ clearInterval(this._batteryCheckTimer);
490
+ this._batteryCheckTimer = null;
488
491
  }
492
+ }
489
493
  }
490
494
 
491
495
  module.exports = {
492
- BatteryOptimizer,
493
- BATTERY_MODE,
494
- BATTERY_THRESHOLDS,
495
- DEFAULT_PROFILES,
496
- DEFAULT_CONFIG,
496
+ BatteryOptimizer,
497
+ BATTERY_MODE,
498
+ BATTERY_THRESHOLDS,
499
+ DEFAULT_PROFILES,
500
+ DEFAULT_CONFIG
497
501
  };