0
# Device Sensors
1
2
Access to device sensors including accelerometer, gyroscope, compass, battery status, vibration, and other hardware sensors for motion detection and device state monitoring.
3
4
## Capabilities
5
6
### Accelerometer (Device Motion)
7
8
Monitor device acceleration and movement for gesture recognition, orientation detection, and motion-based interactions.
9
10
```typescript { .api }
11
/**
12
* Acceleration data interface
13
*/
14
interface Acceleration {
15
/** Acceleration along the x-axis in m/s² */
16
x: number;
17
/** Acceleration along the y-axis in m/s² */
18
y: number;
19
/** Acceleration along the z-axis in m/s² */
20
z: number;
21
/** Timestamp when acceleration was captured */
22
timestamp: number;
23
}
24
25
/**
26
* Accelerometer configuration options
27
*/
28
interface AccelerometerOptions {
29
/** Update frequency in milliseconds (default: 10000) */
30
frequency?: number;
31
}
32
33
/**
34
* DeviceMotion class for accelerometer access
35
*/
36
class DeviceMotion {
37
/**
38
* Get current acceleration values
39
* @returns Promise resolving to current Acceleration data
40
*/
41
static getCurrentAcceleration(): Promise<Acceleration>;
42
43
/**
44
* Watch acceleration changes continuously
45
* @param options Accelerometer configuration options
46
* @returns Observable emitting Acceleration updates
47
*/
48
static watchAcceleration(options?: AccelerometerOptions): Observable<Acceleration>;
49
}
50
```
51
52
**Usage Examples:**
53
54
```typescript
55
import { DeviceMotion, AccelerometerOptions } from 'ionic-native';
56
57
// Get current acceleration
58
async function getCurrentMotion() {
59
try {
60
const acceleration = await DeviceMotion.getCurrentAcceleration();
61
62
console.log('Current acceleration:');
63
console.log(`X: ${acceleration.x.toFixed(3)} m/s²`);
64
console.log(`Y: ${acceleration.y.toFixed(3)} m/s²`);
65
console.log(`Z: ${acceleration.z.toFixed(3)} m/s²`);
66
67
return acceleration;
68
} catch (error) {
69
console.error('Error getting acceleration:', error);
70
throw error;
71
}
72
}
73
74
// Monitor device motion for shake detection
75
function setupShakeDetection() {
76
const options: AccelerometerOptions = {
77
frequency: 100 // Update every 100ms
78
};
79
80
let lastAcceleration = { x: 0, y: 0, z: 0, timestamp: 0 };
81
const shakeThreshold = 15; // m/s²
82
83
return DeviceMotion.watchAcceleration(options).subscribe(
84
(acceleration) => {
85
// Calculate acceleration difference
86
const deltaX = Math.abs(acceleration.x - lastAcceleration.x);
87
const deltaY = Math.abs(acceleration.y - lastAcceleration.y);
88
const deltaZ = Math.abs(acceleration.z - lastAcceleration.z);
89
90
const totalDelta = deltaX + deltaY + deltaZ;
91
92
if (totalDelta > shakeThreshold) {
93
console.log('Shake detected!');
94
handleShakeEvent();
95
}
96
97
lastAcceleration = acceleration;
98
},
99
(error) => {
100
console.error('Accelerometer error:', error);
101
}
102
);
103
}
104
105
// Motion-based gesture recognition
106
class MotionGestureDetector {
107
private subscription: any;
108
private motionBuffer: Acceleration[] = [];
109
private readonly bufferSize = 20;
110
111
startDetection() {
112
const options: AccelerometerOptions = { frequency: 50 };
113
114
this.subscription = DeviceMotion.watchAcceleration(options).subscribe(
115
(acceleration) => {
116
this.processMotionData(acceleration);
117
}
118
);
119
}
120
121
stopDetection() {
122
if (this.subscription) {
123
this.subscription.unsubscribe();
124
}
125
this.motionBuffer = [];
126
}
127
128
private processMotionData(acceleration: Acceleration) {
129
// Add to buffer
130
this.motionBuffer.push(acceleration);
131
132
// Keep buffer size manageable
133
if (this.motionBuffer.length > this.bufferSize) {
134
this.motionBuffer.shift();
135
}
136
137
// Analyze gestures
138
this.detectGestures();
139
}
140
141
private detectGestures() {
142
if (this.motionBuffer.length < 10) return;
143
144
// Detect shake
145
if (this.detectShake()) {
146
this.onGestureDetected('shake');
147
}
148
149
// Detect tilt
150
const tiltDirection = this.detectTilt();
151
if (tiltDirection) {
152
this.onGestureDetected('tilt', tiltDirection);
153
}
154
155
// Detect tap
156
if (this.detectTap()) {
157
this.onGestureDetected('tap');
158
}
159
}
160
161
private detectShake(): boolean {
162
const recent = this.motionBuffer.slice(-5);
163
const avgAcceleration = recent.reduce((acc, curr) => {
164
return Math.abs(curr.x) + Math.abs(curr.y) + Math.abs(curr.z);
165
}, 0) / recent.length;
166
167
return avgAcceleration > 20;
168
}
169
170
private detectTilt(): string | null {
171
const latest = this.motionBuffer[this.motionBuffer.length - 1];
172
173
if (Math.abs(latest.x) > 7) {
174
return latest.x > 0 ? 'left' : 'right';
175
}
176
177
if (Math.abs(latest.y) > 7) {
178
return latest.y > 0 ? 'forward' : 'backward';
179
}
180
181
return null;
182
}
183
184
private detectTap(): boolean {
185
if (this.motionBuffer.length < 3) return false;
186
187
const recent = this.motionBuffer.slice(-3);
188
const zAccelerations = recent.map(a => Math.abs(a.z));
189
190
// Look for spike in Z acceleration (tap)
191
const maxZ = Math.max(...zAccelerations);
192
return maxZ > 15;
193
}
194
195
private onGestureDetected(gesture: string, direction?: string) {
196
console.log(`Gesture detected: ${gesture}`, direction ? `(${direction})` : '');
197
198
// Trigger appropriate actions
199
switch (gesture) {
200
case 'shake':
201
this.handleShake();
202
break;
203
case 'tilt':
204
this.handleTilt(direction!);
205
break;
206
case 'tap':
207
this.handleTap();
208
break;
209
}
210
}
211
212
private handleShake() {
213
// Implement shake action (e.g., refresh, undo)
214
}
215
216
private handleTilt(direction: string) {
217
// Implement tilt actions (e.g., navigation, scrolling)
218
}
219
220
private handleTap() {
221
// Implement tap action (e.g., select, activate)
222
}
223
}
224
```
225
226
### Compass (Device Orientation)
227
228
Access device compass and magnetometer for heading information and navigation features.
229
230
```typescript { .api }
231
/**
232
* Compass heading data
233
*/
234
interface CompassHeading {
235
/** Magnetic heading in degrees (0-359.99) */
236
magneticHeading: number;
237
/** True heading in degrees (0-359.99) */
238
trueHeading: number;
239
/** Heading accuracy in degrees */
240
headingAccuracy: number;
241
/** Timestamp when heading was captured */
242
timestamp: number;
243
}
244
245
/**
246
* Compass error information
247
*/
248
interface CompassError {
249
/** Error code (0: COMPASS_INTERNAL_ERR, 20: COMPASS_NOT_SUPPORTED) */
250
code: number;
251
}
252
253
/**
254
* Compass configuration options
255
*/
256
interface CompassOptions {
257
/** Update frequency in milliseconds (default: 100) */
258
frequency?: number;
259
/** Compass filter value (iOS only, default: 1) */
260
filter?: number;
261
}
262
263
/**
264
* DeviceOrientation class for compass access
265
*/
266
class DeviceOrientation {
267
/**
268
* Get current compass heading
269
* @returns Promise resolving to current CompassHeading
270
*/
271
static getCurrentHeading(): Promise<CompassHeading>;
272
273
/**
274
* Watch compass heading changes
275
* @param options Compass configuration options
276
* @returns Observable emitting CompassHeading updates
277
*/
278
static watchHeading(options?: CompassOptions): Observable<CompassHeading>;
279
}
280
```
281
282
**Usage Examples:**
283
284
```typescript
285
import { DeviceOrientation, CompassOptions } from 'ionic-native';
286
287
// Get current compass heading
288
async function getCurrentHeading() {
289
try {
290
const heading = await DeviceOrientation.getCurrentHeading();
291
292
console.log('Current heading:');
293
console.log(`Magnetic: ${heading.magneticHeading.toFixed(1)}°`);
294
console.log(`True: ${heading.trueHeading.toFixed(1)}°`);
295
console.log(`Accuracy: ±${heading.headingAccuracy.toFixed(1)}°`);
296
297
return heading;
298
} catch (error) {
299
console.error('Error getting compass heading:', error);
300
throw error;
301
}
302
}
303
304
// Compass navigation system
305
class CompassNavigation {
306
private subscription: any;
307
private currentHeading = 0;
308
private targetHeading = 0;
309
310
startCompass(updateCallback?: (heading: number) => void) {
311
const options: CompassOptions = {
312
frequency: 100,
313
filter: 1
314
};
315
316
this.subscription = DeviceOrientation.watchHeading(options).subscribe(
317
(heading) => {
318
this.currentHeading = heading.magneticHeading;
319
320
if (updateCallback) {
321
updateCallback(this.currentHeading);
322
}
323
324
this.updateCompassUI(heading);
325
},
326
(error) => {
327
console.error('Compass error:', error);
328
this.handleCompassError(error);
329
}
330
);
331
}
332
333
stopCompass() {
334
if (this.subscription) {
335
this.subscription.unsubscribe();
336
}
337
}
338
339
setTarget(targetHeading: number) {
340
this.targetHeading = targetHeading;
341
}
342
343
getDirectionToTarget(): { direction: string; degrees: number } {
344
let difference = this.targetHeading - this.currentHeading;
345
346
// Normalize to -180 to 180 range
347
while (difference > 180) difference -= 360;
348
while (difference < -180) difference += 360;
349
350
const absDifference = Math.abs(difference);
351
let direction = '';
352
353
if (absDifference < 5) {
354
direction = 'straight';
355
} else if (difference > 0) {
356
direction = 'right';
357
} else {
358
direction = 'left';
359
}
360
361
return { direction, degrees: absDifference };
362
}
363
364
private updateCompassUI(heading: CompassHeading) {
365
// Update compass rose or navigation arrow
366
const compassElement = document.getElementById('compass');
367
if (compassElement) {
368
compassElement.style.transform = `rotate(${-heading.magneticHeading}deg)`;
369
}
370
371
// Update direction indicator
372
const directionInfo = this.getDirectionToTarget();
373
const directionElement = document.getElementById('direction');
374
if (directionElement) {
375
directionElement.textContent = `${directionInfo.direction} (${directionInfo.degrees.toFixed(1)}°)`;
376
}
377
}
378
379
private handleCompassError(error: any) {
380
if (error.code === 20) {
381
console.error('Compass not supported on this device');
382
} else {
383
console.error('Compass internal error');
384
}
385
}
386
}
387
388
// Compass-based features
389
function getCardinalDirection(heading: number): string {
390
const directions = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE',
391
'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW'];
392
const index = Math.round(heading / 22.5) % 16;
393
return directions[index];
394
}
395
396
function calculateBearing(lat1: number, lon1: number, lat2: number, lon2: number): number {
397
const dLon = (lon2 - lon1) * Math.PI / 180;
398
const lat1Rad = lat1 * Math.PI / 180;
399
const lat2Rad = lat2 * Math.PI / 180;
400
401
const y = Math.sin(dLon) * Math.cos(lat2Rad);
402
const x = Math.cos(lat1Rad) * Math.sin(lat2Rad) -
403
Math.sin(lat1Rad) * Math.cos(lat2Rad) * Math.cos(dLon);
404
405
let bearing = Math.atan2(y, x) * 180 / Math.PI;
406
return (bearing + 360) % 360;
407
}
408
```
409
410
### Gyroscope
411
412
Access device gyroscope for rotation rate detection and advanced motion sensing.
413
414
```typescript { .api }
415
/**
416
* Gyroscope orientation data
417
*/
418
interface GyroscopeOrientation {
419
/** Angular velocity around x-axis in deg/s */
420
x: number;
421
/** Angular velocity around y-axis in deg/s */
422
y: number;
423
/** Angular velocity around z-axis in deg/s */
424
z: number;
425
/** Timestamp when data was captured */
426
timestamp: number;
427
}
428
429
/**
430
* Gyroscope configuration options
431
*/
432
interface GyroscopeOptions {
433
/** Update frequency in milliseconds (default: 100) */
434
frequency?: number;
435
}
436
437
/**
438
* Gyroscope class for rotation sensing
439
*/
440
class Gyroscope {
441
/**
442
* Get current gyroscope data
443
* @param options Gyroscope configuration options
444
* @returns Promise resolving to GyroscopeOrientation
445
*/
446
static getCurrent(options?: GyroscopeOptions): Promise<GyroscopeOrientation>;
447
448
/**
449
* Watch gyroscope changes continuously
450
* @param options Gyroscope configuration options
451
* @returns Observable emitting GyroscopeOrientation updates
452
*/
453
static watch(options?: GyroscopeOptions): Observable<GyroscopeOrientation>;
454
}
455
```
456
457
**Usage Examples:**
458
459
```typescript
460
import { Gyroscope, GyroscopeOptions } from 'ionic-native';
461
462
// Rotation rate monitoring
463
class RotationDetector {
464
private subscription: any;
465
private rotationThreshold = 50; // deg/s
466
467
startMonitoring() {
468
const options: GyroscopeOptions = {
469
frequency: 50
470
};
471
472
this.subscription = Gyroscope.watch(options).subscribe(
473
(orientation) => {
474
this.processRotationData(orientation);
475
},
476
(error) => {
477
console.error('Gyroscope error:', error);
478
}
479
);
480
}
481
482
stopMonitoring() {
483
if (this.subscription) {
484
this.subscription.unsubscribe();
485
}
486
}
487
488
private processRotationData(orientation: GyroscopeOrientation) {
489
const totalRotation = Math.abs(orientation.x) + Math.abs(orientation.y) + Math.abs(orientation.z);
490
491
if (totalRotation > this.rotationThreshold) {
492
this.detectRotationGesture(orientation);
493
}
494
}
495
496
private detectRotationGesture(orientation: GyroscopeOrientation) {
497
// Detect specific rotation patterns
498
if (Math.abs(orientation.z) > this.rotationThreshold) {
499
const direction = orientation.z > 0 ? 'clockwise' : 'counterclockwise';
500
console.log(`Device rotation: ${direction}`);
501
this.onRotationDetected('spin', direction);
502
}
503
504
if (Math.abs(orientation.x) > this.rotationThreshold) {
505
const direction = orientation.x > 0 ? 'forward' : 'backward';
506
console.log(`Device pitch: ${direction}`);
507
this.onRotationDetected('pitch', direction);
508
}
509
510
if (Math.abs(orientation.y) > this.rotationThreshold) {
511
const direction = orientation.y > 0 ? 'left' : 'right';
512
console.log(`Device roll: ${direction}`);
513
this.onRotationDetected('roll', direction);
514
}
515
}
516
517
private onRotationDetected(type: string, direction: string) {
518
// Handle rotation gestures
519
console.log(`Rotation gesture: ${type} ${direction}`);
520
}
521
}
522
```
523
524
### Battery Status
525
526
Monitor device battery level and charging status for power management features.
527
528
```typescript { .api }
529
/**
530
* Battery status data
531
*/
532
interface StatusObject {
533
/** Battery level (0-100) */
534
level: number;
535
/** Whether device is plugged in */
536
isPlugged: boolean;
537
}
538
539
/**
540
* BatteryStatus class for battery monitoring
541
*/
542
class BatteryStatus {
543
/**
544
* Monitor battery status changes
545
* @returns Observable emitting battery status updates
546
*/
547
static onStatusChange(): Observable<StatusObject>;
548
549
/**
550
* Monitor low battery events
551
* @returns Observable emitting low battery events
552
*/
553
static onLow(): Observable<StatusObject>;
554
555
/**
556
* Monitor critical battery events
557
* @returns Observable emitting critical battery events
558
*/
559
static onCritical(): Observable<StatusObject>;
560
}
561
```
562
563
**Usage Examples:**
564
565
```typescript
566
import { BatteryStatus } from 'ionic-native';
567
568
// Battery monitoring service
569
class BatteryMonitor {
570
private statusSubscription: any;
571
private lowBatterySubscription: any;
572
private criticalBatterySubscription: any;
573
574
startMonitoring() {
575
// Monitor general status changes
576
this.statusSubscription = BatteryStatus.onStatusChange().subscribe(
577
(status) => {
578
this.updateBatteryUI(status);
579
this.handleBatteryStatus(status);
580
}
581
);
582
583
// Monitor low battery
584
this.lowBatterySubscription = BatteryStatus.onLow().subscribe(
585
(status) => {
586
this.handleLowBattery(status);
587
}
588
);
589
590
// Monitor critical battery
591
this.criticalBatterySubscription = BatteryStatus.onCritical().subscribe(
592
(status) => {
593
this.handleCriticalBattery(status);
594
}
595
);
596
}
597
598
stopMonitoring() {
599
if (this.statusSubscription) this.statusSubscription.unsubscribe();
600
if (this.lowBatterySubscription) this.lowBatterySubscription.unsubscribe();
601
if (this.criticalBatterySubscription) this.criticalBatterySubscription.unsubscribe();
602
}
603
604
private updateBatteryUI(status: StatusObject) {
605
// Update battery indicator
606
const batteryElement = document.getElementById('battery-level');
607
if (batteryElement) {
608
batteryElement.textContent = `${status.level}%`;
609
batteryElement.className = `battery-level ${this.getBatteryClass(status.level)}`;
610
}
611
612
// Update charging indicator
613
const chargingElement = document.getElementById('charging-status');
614
if (chargingElement) {
615
chargingElement.textContent = status.isPlugged ? 'Charging' : 'Not Charging';
616
}
617
}
618
619
private getBatteryClass(level: number): string {
620
if (level > 80) return 'high';
621
if (level > 50) return 'medium';
622
if (level > 20) return 'low';
623
return 'critical';
624
}
625
626
private handleBatteryStatus(status: StatusObject) {
627
// Adjust app behavior based on battery level
628
if (status.level < 20 && !status.isPlugged) {
629
this.enablePowerSavingMode();
630
} else if (status.level > 30 || status.isPlugged) {
631
this.disablePowerSavingMode();
632
}
633
}
634
635
private handleLowBattery(status: StatusObject) {
636
console.warn('Low battery warning:', status);
637
638
// Show low battery notification
639
this.showBatteryWarning('Battery Low', `Battery level: ${status.level}%. Please charge your device.`);
640
641
// Enable aggressive power saving
642
this.enablePowerSavingMode();
643
}
644
645
private handleCriticalBattery(status: StatusObject) {
646
console.error('Critical battery warning:', status);
647
648
// Show critical battery alert
649
this.showBatteryWarning('Critical Battery', `Battery critically low: ${status.level}%. Save your work immediately.`);
650
651
// Enable emergency power saving
652
this.enableEmergencyMode();
653
}
654
655
private enablePowerSavingMode() {
656
console.log('Enabling power saving mode');
657
658
// Reduce app functionality to save battery
659
// - Lower screen brightness
660
// - Reduce sync frequency
661
// - Disable animations
662
// - Limit background processes
663
}
664
665
private disablePowerSavingMode() {
666
console.log('Disabling power saving mode');
667
668
// Restore normal app functionality
669
}
670
671
private enableEmergencyMode() {
672
console.log('Enabling emergency mode');
673
674
// Minimal functionality only
675
// - Auto-save data
676
// - Disable non-essential features
677
// - Show power saving tips
678
}
679
680
private showBatteryWarning(title: string, message: string) {
681
// Show appropriate warning UI
682
// Could use Toast, Dialogs, or custom notification
683
}
684
}
685
```
686
687
### Vibration
688
689
Control device vibration for haptic feedback and notifications.
690
691
```typescript { .api }
692
/**
693
* Vibration class for haptic feedback
694
*/
695
class Vibration {
696
/**
697
* Vibrate device
698
* @param time Vibration duration in milliseconds, or array of durations for pattern
699
*/
700
static vibrate(time: number | number[]): void;
701
}
702
```
703
704
**Usage Examples:**
705
706
```typescript
707
import { Vibration } from 'ionic-native';
708
709
// Haptic feedback patterns
710
class HapticFeedback {
711
// Success feedback
712
static success() {
713
Vibration.vibrate(100);
714
}
715
716
// Error feedback
717
static error() {
718
Vibration.vibrate([100, 50, 100]);
719
}
720
721
// Warning feedback
722
static warning() {
723
Vibration.vibrate([200, 100, 200]);
724
}
725
726
// Notification feedback
727
static notification() {
728
Vibration.vibrate([50, 50, 50]);
729
}
730
731
// Custom pattern
732
static customPattern(pattern: number[]) {
733
Vibration.vibrate(pattern);
734
}
735
736
// Long press feedback
737
static longPress() {
738
Vibration.vibrate(50);
739
}
740
741
// Button tap feedback
742
static tap() {
743
Vibration.vibrate(30);
744
}
745
746
// Heartbeat pattern
747
static heartbeat() {
748
Vibration.vibrate([100, 100, 200, 100, 300]);
749
}
750
751
// SOS pattern
752
static sos() {
753
// S-O-S in morse code
754
Vibration.vibrate([
755
100, 50, 100, 50, 100, // S
756
200, 300, 50, 300, 50, 300, // O
757
200, 100, 50, 100, 50, 100 // S
758
]);
759
}
760
}
761
762
// Usage examples
763
function showHapticExamples() {
764
// Basic vibrations
765
HapticFeedback.success();
766
HapticFeedback.error();
767
HapticFeedback.warning();
768
769
// Custom patterns
770
HapticFeedback.customPattern([200, 100, 200, 100, 400]);
771
772
// Contextual feedback
773
HapticFeedback.tap(); // For button presses
774
HapticFeedback.longPress(); // For long press gestures
775
}
776
```
777
778
### Flashlight
779
780
Control device LED flashlight for utility and emergency features.
781
782
```typescript { .api }
783
/**
784
* Flashlight class for LED control
785
*/
786
class Flashlight {
787
/**
788
* Check if flashlight is available
789
* @returns Promise resolving to availability status
790
*/
791
static available(): Promise<boolean>;
792
793
/**
794
* Turn flashlight on
795
* @returns Promise resolving to success status
796
*/
797
static switchOn(): Promise<boolean>;
798
799
/**
800
* Turn flashlight off
801
* @returns Promise resolving to success status
802
*/
803
static switchOff(): Promise<boolean>;
804
805
/**
806
* Toggle flashlight state
807
* @returns Promise resolving to new state (true = on)
808
*/
809
static toggle(): Promise<boolean>;
810
}
811
```
812
813
**Usage Examples:**
814
815
```typescript
816
import { Flashlight } from 'ionic-native';
817
818
// Flashlight controller
819
class FlashlightController {
820
private isOn = false;
821
822
async initialize() {
823
try {
824
const available = await Flashlight.available();
825
if (!available) {
826
console.log('Flashlight not available on this device');
827
return false;
828
}
829
830
console.log('Flashlight available');
831
return true;
832
} catch (error) {
833
console.error('Error checking flashlight availability:', error);
834
return false;
835
}
836
}
837
838
async turnOn() {
839
try {
840
const success = await Flashlight.switchOn();
841
if (success) {
842
this.isOn = true;
843
console.log('Flashlight turned on');
844
}
845
return success;
846
} catch (error) {
847
console.error('Error turning on flashlight:', error);
848
return false;
849
}
850
}
851
852
async turnOff() {
853
try {
854
const success = await Flashlight.switchOff();
855
if (success) {
856
this.isOn = false;
857
console.log('Flashlight turned off');
858
}
859
return success;
860
} catch (error) {
861
console.error('Error turning off flashlight:', error);
862
return false;
863
}
864
}
865
866
async toggle() {
867
try {
868
const newState = await Flashlight.toggle();
869
this.isOn = newState;
870
console.log(`Flashlight ${newState ? 'on' : 'off'}`);
871
return newState;
872
} catch (error) {
873
console.error('Error toggling flashlight:', error);
874
return this.isOn;
875
}
876
}
877
878
getState(): boolean {
879
return this.isOn;
880
}
881
882
// Morse code signaling
883
async morseCode(message: string) {
884
const morseMap: { [key: string]: string } = {
885
'A': '.-', 'B': '-...', 'C': '-.-.', 'D': '-..', 'E': '.', 'F': '..-.',
886
'G': '--.', 'H': '....', 'I': '..', 'J': '.---', 'K': '-.-', 'L': '.-..',
887
'M': '--', 'N': '-.', 'O': '---', 'P': '.--.', 'Q': '--.-', 'R': '.-.',
888
'S': '...', 'T': '-', 'U': '..-', 'V': '...-', 'W': '.--', 'X': '-..-',
889
'Y': '-.--', 'Z': '--..', '0': '-----', '1': '.----', '2': '..---',
890
'3': '...--', '4': '....-', '5': '.....', '6': '-....', '7': '--...',
891
'8': '---..', '9': '----.', ' ': '/'
892
};
893
894
for (const char of message.toUpperCase()) {
895
const morse = morseMap[char];
896
if (morse) {
897
await this.sendMorseSequence(morse);
898
await this.delay(200); // Inter-character pause
899
}
900
}
901
}
902
903
private async sendMorseSequence(morse: string) {
904
for (const symbol of morse) {
905
if (symbol === '.') {
906
await this.flash(100); // Dot
907
} else if (symbol === '-') {
908
await this.flash(300); // Dash
909
} else if (symbol === '/') {
910
await this.delay(500); // Word separator
911
}
912
await this.delay(100); // Inter-element pause
913
}
914
}
915
916
private async flash(duration: number) {
917
await this.turnOn();
918
await this.delay(duration);
919
await this.turnOff();
920
}
921
922
private delay(ms: number): Promise<void> {
923
return new Promise(resolve => setTimeout(resolve, ms));
924
}
925
926
// SOS emergency signal
927
async sos() {
928
const sosPattern = '... --- ...'; // SOS in morse
929
await this.morseCode('SOS');
930
}
931
932
// Strobe effect
933
async strobe(duration: number = 5000, frequency: number = 10) {
934
const interval = 1000 / frequency;
935
const endTime = Date.now() + duration;
936
937
while (Date.now() < endTime) {
938
await this.toggle();
939
await this.delay(interval / 2);
940
}
941
942
// Ensure flashlight is off when done
943
if (this.isOn) {
944
await this.turnOff();
945
}
946
}
947
}
948
```