forked from yudi_xiao/expo-ble-app-demo
373 lines
11 KiB
TypeScript
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;
|