HarmonyOS 5.0以降では、デバイスのハードウェア機能を安全かつ効率的に活用するための統一APIが提供されている。本稿では、センサーデータ取得、Bluetooth通信、位置情報サービスの実装方法を解説する。
1. センサーデータの取得
HarmonyOSは複数のセンサータイプに対応しており、@ohos.sensorモジュールを通じてアクセス可能である。
import sensor from '@ohos.sensor';
import { BusinessError } from '@ohos.base';
class MotionDetector {
private manager: sensor.SensorManager | null = null;
private listener: sensor.SensorEventListener | null = null;
async init(): Promise<void> {
try {
this.manager = await sensor.getSensorManager();
} catch (err) {
console.error('センサー初期化失敗:', (err as BusinessError).message);
}
}
async startMonitoring(): Promise<void> {
if (!this.manager) return;
const accel = await this.manager.getDefaultSensor(sensor.SensorType.SENSOR_TYPE_ACCELEROMETER);
this.listener = await this.manager.registerListener(
accel,
(event) => {
const [x, y, z] = event.data;
const magnitude = Math.sqrt(x ** 2 + y ** 2 + z ** 2);
if (magnitude > 14.0) {
console.log('急激な動きを検出');
}
},
{ interval: 200 }
);
}
async stop(): Promise<void> {
if (this.manager && this.listener) {
await this.manager.unregisterListener(this.listener);
}
}
}
2. Bluetooth通信の実装
Bluetooth ClassicおよびBLE(Bluetooth Low Energy)の両方に対応している。
2.1 Bluetooth Classicによるデータ送受信
import bluetooth from '@ohos.bluetooth';
class RfcommClient {
private socketMap = new Map<string, bluetooth.BluetoothSocket>();
async connect(device: bluetooth.BluetoothDevice): Promise<boolean> {
try {
const sock = await device.createRfcommSocketToServiceRecord(
'00001101-0000-1000-8000-00805F9B34FB'
);
await sock.connect();
this.socketMap.set(device.deviceId, sock);
sock.on('message', (buf: Uint8Array) => {
const text = new TextDecoder().decode(buf);
console.log(`受信: ${text}`);
});
return true;
} catch (e) {
console.error('接続失敗:', (e as BusinessError).message);
return false;
}
}
async send(id: string, msg: string): Promise<boolean> {
const sock = this.socketMap.get(id);
if (!sock) return false;
try {
await sock.send(new TextEncoder().encode(msg));
return true;
} catch (e) {
console.error('送信エラー:', (e as BusinessError).message);
return false;
}
}
}
2.2 BLEデバイスとの連携
import ble from '@ohos.bluetooth.ble';
class BleHeartRateMonitor {
async scanAndConnect(): Promise<void> {
const scanner = await ble.createScanner();
scanner.on('deviceDiscover', async (dev) => {
if (dev.name?.includes('HRM')) {
await scanner.stopScan();
const client = await ble.createGattClientDevice(dev.deviceId);
await client.connect();
const services = await client.discoverServices();
for (const svc of services) {
for (const chr of svc.characteristics) {
if (chr.properties.notify) {
await client.subscribeToCharacteristic(chr);
}
}
}
}
});
await scanner.startScan();
setTimeout(() => scanner.stopScan(), 8000);
}
}
3. 位置情報サービスの活用
@ohos.geolocationモジュールにより、高精度または省電力モードでの位置取得が可能。
3.1 一時的な位置取得
import geolocation from '@ohos.geolocation';
async function fetchLocation(): Promise<geolocation.Location | null> {
const mgr = geolocation.getLocationManager();
const req: geolocation.CurrentLocationRequest = {
priority: geolocation.LocationRequestPriority.FAST_FIRST_FIX,
maxAccuracy: 30,
timeout: 8000
};
try {
const loc = await mgr.getCurrentLocation(req);
console.log(`緯度: ${loc.latitude}, 経度: ${loc.longitude}`);
return loc;
} catch (e) {
console.error('位置取得失敗:', (e as BusinessError).message);
return null;
}
}
3.2 持続的な位置監視とジオフェンス
class GeoTracker {
private mgr: geolocation.LocationManager;
private fenceCenter = { lat: 35.6895, lon: 139.6917 };
private radiusMeters = 500;
constructor() {
this.mgr = geolocation.getLocationManager();
}
async startTracking(): Promise<void> {
const req: geolocation.LocationRequest = {
priority: geolocation.LocationRequestPriority.BALANCED_POWER_ACCURACY,
timeInterval: 6000,
distanceInterval: 15
};
await this.mgr.requestLocationUpdates(req, (loc) => {
const dist = this.haversineDistance(
loc.latitude, loc.longitude,
this.fenceCenter.lat, this.fenceCenter.lon
);
if (dist <= this.radiusMeters) {
console.log('ジオフェンス内に進入');
}
});
}
private haversineDistance(lat1: number, lon1: number, lat2: number, lon2: number): number {
const R = 6371000;
const φ1 = lat1 * Math.PI / 180;
const φ2 = lat2 * Math.PI / 180;
const Δφ = (lat2 - lat1) * Math.PI / 180;
const Δλ = (lon2 - lon1) * Math.PI / 180;
const a = Math.sin(Δφ / 2) ** 2 +
Math.cos(φ1) * Math.cos(φ2) *
Math.sin(Δλ / 2) ** 2;
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return R * c;
}
}
4. エラーハンドリングとパフォーマンス最適化
ハードウェア操作では、権限不足やリソース競合などのエラーが発生しやすいため、包括的なエラーハンドリングが必要。
class SafeHardwareInvoker {
static async execute<T>(name: string, fn: () => Promise<T>): Promise<T | null> {
try {
return await fn();
} catch (err) {
const e = err as BusinessError;
switch (e.code) {
case 201:
console.warn('必要な権限が付与されていません');
break;
case 202:
console.warn('このデバイスは対応していません');
break;
default:
console.error(`${name}で未定義エラー: ${e.message}`);
}
return null;
}
}
}
// 使用例
const loc = await SafeHardwareInvoker.execute('位置取得', fetchLocation);
5. プライバシー保護対策
位置情報やセンサーデータは個人を特定可能な情報となるため、匿名化や最小限の収集が推奨される。
function anonymize(loc: geolocation.Location) {
return {
lat: Math.floor(loc.latitude * 100) / 100, // 小数点第2位以下切り捨て
lon: Math.floor(loc.longitude * 100) / 100,
accuracy: Math.ceil(loc.accuracy / 50) * 50 // 精度を50m単位に丸め
};
}