Files
expo-ble-app-demo/ble/example/BleExampleV2.tsx
2025-12-10 10:29:50 +08:00

373 lines
11 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import { View, Text, Button, Alert, ScrollView, StyleSheet } from 'react-native';
import {
BleManager,
DeviceInfoManagerV2,
FileTransferManager,
CommandUtils,
PROTOCOL_UUIDS,
APP_COMMAND_TYPES,
DEVICE_RESPONSE_TYPES,
BleEventCallback,
DeviceInfoListener,
FileTransferListener,
DeviceInfo,
ActivationStatus,
VersionInfo,
BleDevice,
} from '../index';
/**
* Example component demonstrating BLE V2.1.1 protocol usage
* This component shows how to use the new JSON-based BLE protocol
*/
const BleExampleV2: React.FC = () => {
const [isScanning, setIsScanning] = useState(false);
const [isConnected, setIsConnected] = useState(false);
const [connectedDevice, setConnectedDevice] = useState<BleDevice | null>(null);
const [deviceInfo, setDeviceInfo] = useState<DeviceInfo | null>(null);
const [version, setVersion] = useState<string>('');
const [isActivated, setIsActivated] = useState<boolean>(false);
const [transferProgress, setTransferProgress] = useState<number>(0);
const [isTransferring, setIsTransferring] = useState<boolean>(false);
const [logs, setLogs] = useState<string[]>([]);
// Managers
const bleManager = BleManager.getInstance();
const deviceInfoManager = DeviceInfoManagerV2.getInstance();
const fileTransferManager = FileTransferManager.getInstance();
// Add log message
const addLog = (message: string) => {
const timestamp = new Date().toLocaleTimeString();
setLogs(prev => [...prev, `[${timestamp}] ${message}`]);
};
// Setup event listeners
useEffect(() => {
const bleCallback: BleEventCallback = {
onDiscoveryStateChange: (scanning: boolean) => {
setIsScanning(scanning);
addLog(`Scanning: ${scanning ? 'Started' : 'Stopped'}`);
},
onDeviceDiscovered: (device) => {
addLog(`Device discovered: ${device.name || 'Unknown'} (${device.id})`);
},
onConnectionStateChange: (deviceId, status) => {
setIsConnected(status === 'Connected');
addLog(`Connection ${status}: ${deviceId}`);
if (status === 'Disconnected') {
setConnectedDevice(null);
}
},
onDataReceived: (deviceId, serviceUuid, characteristicUuid, data) => {
console.log('onDataReceived', deviceId, serviceUuid, characteristicUuid, data);
addLog(`Data received from ${deviceId} (${data.byteLength} bytes)`);
},
};
const deviceInfoCallback: DeviceInfoListener = {
onDeviceInfoReceived: (info) => {
setDeviceInfo(info);
addLog(`Device info received: ${info.devname}`);
},
onVersionReceived: (version) => {
setVersion(version);
addLog(`Version: ${version}`);
},
onActivationStatusReceived: (activated) => {
setIsActivated(activated);
addLog(`Activation status: ${activated ? 'Activated' : 'Not activated'}`);
},
onIdentityCheckReceived: (isValid) => {
addLog(`Identity check: ${isValid ? 'Valid' : 'Invalid'}`);
},
};
const fileTransferCallback: FileTransferListener = {
onStart: () => {
setIsTransferring(true);
setTransferProgress(0);
addLog('File transfer started');
},
onProgress: (progress) => {
setTransferProgress(progress);
addLog(`Transfer progress: ${progress}%`);
},
onComplete: () => {
setIsTransferring(false);
setTransferProgress(0);
addLog('File transfer completed');
},
onFail: (code, message) => {
setIsTransferring(false);
setTransferProgress(0);
addLog(`Transfer failed: ${message} (${code})`);
},
};
bleManager.registerCallback(bleCallback);
deviceInfoManager.addListener(deviceInfoCallback);
fileTransferManager.addListener(fileTransferCallback);
return () => {
bleManager.unregisterCallback(bleCallback);
deviceInfoManager.removeListener(deviceInfoCallback);
fileTransferManager.removeListener(fileTransferCallback);
};
}, []);
// Start scanning for devices
const startScan = async () => {
try {
addLog('Starting BLE scan...');
await bleManager.startScan();
} catch (error) {
addLog(`Scan failed: ${error}`);
}
};
// Stop scanning
const stopScan = async () => {
try {
addLog('Stopping BLE scan...');
await bleManager.stopScan();
} catch (error) {
addLog(`Stop scan failed: ${error}`);
}
};
// Connect to device (for demo, connect to first discovered device)
const connectToDevice = async () => {
try {
addLog('Connecting to device...');
// In real app, you would select a specific device
// For demo, we'll show the connection flow
Alert.alert('Connect', 'Please select a device from the scan results first');
} catch (error) {
addLog(`Connection failed: ${error}`);
}
};
// Disconnect from device
const disconnectDevice = async () => {
try {
addLog('Disconnecting from device...');
await bleManager.disconnectDevice();
} catch (error) {
addLog(`Disconnect failed: ${error}`);
}
};
// Query activation status
const queryActivationStatus = async () => {
try {
addLog('Querying activation status...');
await deviceInfoManager.queryActivationStatus();
} catch (error) {
addLog(`Activation query failed: ${error}`);
}
};
// Query version
const queryVersion = async () => {
try {
addLog('Querying device version...');
await deviceInfoManager.queryVersion();
} catch (error) {
addLog(`Version query failed: ${error}`);
}
};
// Request device info
const requestDeviceInfo = async () => {
try {
addLog('Requesting device info...');
await deviceInfoManager.requestDeviceInfoReport();
} catch (error) {
addLog(`Device info request failed: ${error}`);
}
};
// Update activation time to current time
const updateActivationTime = async () => {
try {
addLog('Updating activation time...');
await CommandUtils.sendCurrentTimeUpdate();
} catch (error) {
addLog(`Time update failed: ${error}`);
}
};
// Send identity check response
const sendIdentityCheck = (isValid: boolean) => {
try {
addLog(`Sending identity check response: ${isValid}`);
deviceInfoManager.sendIdentityCheckResponse(isValid);
} catch (error) {
addLog(`Identity check failed: ${error}`);
}
};
// Transfer sample file (demo)
const transferSampleFile = async () => {
try {
addLog('Starting sample file transfer...');
// In real app, you would provide actual file path
Alert.alert('File Transfer', 'Please provide a valid file path for transfer');
} catch (error) {
addLog(`File transfer failed: ${error}`);
}
};
// Clear logs
const clearLogs = () => {
setLogs([]);
};
return (
<ScrollView style={styles.container}>
<Text style={styles.title}>BLE V2.1.1 Protocol Example</Text>
{/* Connection Status */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Connection Status</Text>
<Text>Scanning: {isScanning ? 'Yes' : 'No'}</Text>
<Text>Connected: {isConnected ? 'Yes' : 'No'}</Text>
<Text>Device: {connectedDevice?.name || 'None'}</Text>
</View>
{/* Device Info */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Device Information</Text>
<Text>Activated: {isActivated ? 'Yes' : 'No'}</Text>
<Text>Version: {version || 'Unknown'}</Text>
{deviceInfo && (
<>
<Text>Name: {deviceInfo.devname}</Text>
<Text>Total Space: {deviceInfo.allspace} KB</Text>
<Text>Free Space: {deviceInfo.freespace} KB</Text>
<Text>Brand: {deviceInfo.brand}</Text>
</>
)}
</View>
{/* Transfer Status */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>File Transfer</Text>
<Text>Transferring: {isTransferring ? 'Yes' : 'No'}</Text>
<Text>Progress: {transferProgress}%</Text>
</View>
{/* Control Buttons */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Controls</Text>
<View style={styles.buttonRow}>
<Button
title={isScanning ? "Stop Scan" : "Start Scan"}
onPress={isScanning ? stopScan : startScan}
/>
<Button
title={isConnected ? "Disconnect" : "Connect"}
onPress={isConnected ? disconnectDevice : connectToDevice}
disabled={!isConnected && !isScanning}
/>
</View>
<View style={styles.buttonRow}>
<Button title="Query Activation" onPress={queryActivationStatus} disabled={!isConnected} />
<Button title="Query Version" onPress={queryVersion} disabled={!isConnected} />
</View>
<View style={styles.buttonRow}>
<Button title="Device Info" onPress={requestDeviceInfo} disabled={!isConnected} />
<Button title="Update Time" onPress={updateActivationTime} disabled={!isConnected} />
</View>
<View style={styles.buttonRow}>
<Button title="Identity Check (Valid)" onPress={() => sendIdentityCheck(true)} disabled={!isConnected} />
<Button title="Identity Check (Invalid)" onPress={() => sendIdentityCheck(false)} disabled={!isConnected} />
</View>
<View style={styles.buttonRow}>
<Button title="Transfer File" onPress={transferSampleFile} disabled={!isConnected} />
<Button title="Clear Logs" onPress={clearLogs} />
</View>
</View>
{/* Protocol Info */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Protocol Information</Text>
<Text>Version: 2.1.1</Text>
<Text>Service UUID: {PROTOCOL_UUIDS.SERVICE}</Text>
<Text>Write UUID: {PROTOCOL_UUIDS.WRITE_CHARACTERISTIC}</Text>
<Text>Read UUID: {PROTOCOL_UUIDS.READ_CHARACTERISTIC}</Text>
</View>
{/* Logs */}
<View style={styles.section}>
<View style={styles.logHeader}>
<Text style={styles.sectionTitle}>Logs</Text>
<Button title="Clear" onPress={clearLogs} />
</View>
<ScrollView style={styles.logContainer} nestedScrollEnabled={true}>
{logs.map((log, index) => (
<Text key={index} style={styles.logText}>{log}</Text>
))}
</ScrollView>
</View>
</ScrollView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 16,
backgroundColor: '#f5f5f5',
},
title: {
fontSize: 20,
fontWeight: 'bold',
textAlign: 'center',
marginBottom: 20,
},
section: {
backgroundColor: 'white',
padding: 16,
marginBottom: 16,
borderRadius: 8,
elevation: 2,
},
sectionTitle: {
fontSize: 16,
fontWeight: 'bold',
marginBottom: 8,
},
buttonRow: {
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: 8,
},
logHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 8,
},
logContainer: {
height: 200,
backgroundColor: '#f8f8f8',
padding: 8,
borderRadius: 4,
},
logText: {
fontSize: 12,
fontFamily: 'monospace',
marginBottom: 2,
},
});
export default BleExampleV2;