// src/ScanScreen.tsx此页面为功率计搜索页面 import React, { useEffect, useState, useCallback, useRef } from "react"; import { View, Text, FlatList, TouchableOpacity, StyleSheet, RefreshControl, } from "react-native"; import { Central, CentralEventMap, ScannedPeripheral, } from "@systemic-games/react-native-bluetooth-le"; import { NativeStackScreenProps } from "@react-navigation/native-stack"; import { RootStackParamList } from "../App"; import Icon from "react-native-vector-icons/MaterialCommunityIcons"; // 信号图标集 type Props = NativeStackScreenProps; type DeviceWithTimestamp = ScannedPeripheral & { lastSeen: number }; import { useTranslation } from 'react-i18next'; import MyStatusbar from "./component/MyStatusbar"; import MyHeader from "./component/MyHeader"; export default function ScanScreen({ navigation }: Props) { const [devices, setDevices] = useState([]); const [scanning, setScanning] = useState(false); const [refreshing, setRefreshing] = useState(false); const handlerRef = useRef<((payload: CentralEventMap["scannedPeripheral"]) => void) | null>(null); const { t } = useTranslation(); // 将 RSSI 转换为信号格数(0-4) const getSignalLevel = (rssi?: number): number => { if (rssi === undefined) return 0; if (rssi >= -50) return 4; // 很强 if (rssi >= -65) return 3; // 强 if (rssi >= -80) return 2; // 一般 if (rssi >= -90) return 1; // 弱 return 0; // 无信号 }; // 渲染信号图标(格数+颜色) const renderSignalIcon = (rssi?: number) => { const level = getSignalLevel(rssi); let iconColor = "#aaa"; // 默认灰色 switch (level) { case 4: iconColor = "#8BC34A"; // 亮绿 break; case 3: iconColor = "#4CAF50"; // 深绿 break; case 2: iconColor = "#FF9800"; // 橙色 break; case 1: iconColor = "#F44336"; // 红色 break; } const iconName = level === 4 ? "wifi-strength-4" : level === 3 ? "wifi-strength-3" : level === 2 ? "wifi-strength-2" : level === 1 ? "wifi-strength-1" : "wifi-strength-outline"; return ; }; // 更新或添加设备,并记录最后扫描时间 const updatePeripherals = useCallback((p: ScannedPeripheral) => { if (!p?.name || (!p.name.startsWith("POWERFUN") && !p.name.startsWith("PF-PM5"))) return; if ((p.advertisementData?.rssi ?? -999) < -90) return; // 已过滤弱信号 setDevices((prev) => { const now = Date.now(); const exists = prev.find((d) => d.systemId === p.systemId); if (exists) { return prev.map((d) => d.systemId === p.systemId ? { ...p, lastSeen: now } : d ); } else { return [...prev, { ...p, lastSeen: now }]; } }); }, []); // 清理消失或信号低的设备 useEffect(() => { const interval = setInterval(() => { const now = Date.now(); setDevices((prev) => prev.filter( (d) => (d.advertisementData?.rssi ?? -999) >= -90 && now - d.lastSeen < 3000 ) ); }, 1000); return () => clearInterval(interval); }, []); const stopScan = useCallback(() => { setScanning(false); try { Central.stopScan(); } catch {} if (handlerRef.current) { try { Central.removeListener("scannedPeripheral", handlerRef.current); } catch {} handlerRef.current = null; } try { Central.shutdown(); } catch {} }, []); const startScan = useCallback(() => { stopScan(); setDevices([]); setScanning(true); const onScanned = (payload: CentralEventMap["scannedPeripheral"]) => { const p = payload?.peripheral; if (p?.name && p.advertisementData?.isConnectable) { updatePeripherals(p); } }; handlerRef.current = onScanned; try { Central.initialize(); Central.addListener("scannedPeripheral", onScanned); Central.startScan([]); } catch (e) { console.warn("Central scan start error:", e); setScanning(false); handlerRef.current = null; try { Central.shutdown(); } catch {} } }, [stopScan, updatePeripherals]); const onRefresh = useCallback(async () => { setRefreshing(true); stopScan(); await new Promise((resolve) => setTimeout(resolve, 200)); startScan(); setRefreshing(false); }, [stopScan, startScan]); useEffect(() => { startScan(); return () => { stopScan(); }; }, [startScan, stopScan]); return ( item.systemId} renderItem={({ item }) => ( navigation.navigate("Info", { peripheral: item })} style={styles.deviceRow} > {item.name || t("scan.noName")} {renderSignalIcon(item.advertisementData?.rssi)} {item.advertisementData?.rssi ?? "--"} dBm )} ListEmptyComponent={() => ( {scanning ? ( <> {t("scan.scanning")} {t("scan.tipScanning")} ) : ( <> {t("scan.noDevice")} {t("scan.tipBluetooth")} )} )} refreshControl={} /> ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: "#fff" }, deviceRow: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", paddingVertical: 15, paddingHorizontal: 20, borderBottomWidth: 1, borderColor: "#eee", }, deviceInfo: { flexDirection: "column" }, deviceName: { fontSize: 16, fontWeight: "500" }, rssiRow: { flexDirection: "row", alignItems: "center", marginTop: 4 }, rssiText: { fontSize: 14, color: "#666", marginLeft: 6 }, emptyBox: { flex: 1, justifyContent: "center", alignItems: "center", paddingHorizontal: 20, marginTop: 50, }, emptyText: { fontSize: 18, fontWeight: "600", marginBottom: 8 }, tips: { fontSize: 14, color: "#888", textAlign: "center" }, });