or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

analytics-monetization.mdcamera-media.mddevice-sensors.mddevice-system.mdindex.mdinput-hardware.mdlocation-maps.mdnetwork-communication.mdnotifications-ui.mdsecurity-auth.mdsocial-sharing.mdstorage-files.md
tile.json

device-sensors.mddocs/

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

```