import { useEffect, useState, type ReactNode } from "react"; import { Pressable, View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import * as Clipboard from "expo-clipboard"; import { Container } from "@/components/Container"; import { SafeAreaScrollView } from "@/components/safe-area-scroll-view"; import { Button } from "@/components/ui/button"; import { Text } from "@/components/ui/text"; import { Ble } from "@aris/ble"; import { useBleStore } from "@/store/ble"; import { cn } from "@/lib/utils"; import { Ionicons } from "@expo/vector-icons"; const formatTime = (timestamp: number | null) => { if (!timestamp) { return "Never"; } return new Date(timestamp).toLocaleTimeString(); }; export default function BleScreen() { const initialize = useBleStore((state) => state.initialize); const isSupported = useBleStore((state) => state.isSupported); const insets = useSafeAreaInsets(); useEffect(() => { initialize(); }, [initialize]); if (!isSupported) { return ( BLE Bluetooth peripheral status and controls. BLE Unavailable This page is currently supported on iOS only. ); } return ( BLE Bluetooth peripheral status and controls. ); } function ValueRow({ label, value }: { label: ReactNode; value: ReactNode }) { return ( {typeof label === "string" ? ( {label} ) : ( label )} {typeof value === "string" || typeof value === "number" ? ( {value} ) : ( value )} ); } function BleStatusSection() { const bluetoothState = useBleStore((state) => state.bluetoothState); const isSubscribed = useBleStore((state) => state.isSubscribed); const subscribedCount = useBleStore((state) => state.subscribedCount); const connectionLabel = isSubscribed ? "Connected" : "Not Connected"; return ( Status ); } function ControlPanel() { const advertisingEnabled = useBleStore((state) => state.advertisingEnabled); const setAdvertisingEnabled = useBleStore( (state) => state.setAdvertisingEnabled, ); const wifiRequested = useBleStore((state) => state.wifiRequested); const setWifiRequested = useBleStore((state) => state.setWifiRequested); const sendFixtureFeedNow = useBleStore((state) => state.sendFixtureFeedNow); const handleCopyUUIDs = async () => { const uuids = Ble.getUuids(); const text = [ `SERVICE_UUID=${uuids.serviceUUID}`, `FEED_TX_UUID=${uuids.feedTxUUID}`, `CONTROL_RX_UUID=${uuids.controlRxUUID}`, `WIFI_REQUEST_TX_UUID=${uuids.wifiRequestTxUUID}`, ].join("\n"); await Clipboard.setStringAsync(text); }; return ( Control Panel } label="Advertising" checked={advertisingEnabled} onCheckChange={setAdvertisingEnabled} /> } label="WiFi" checked={wifiRequested} onCheckChange={setWifiRequested} /> ); } function ControlTile({ icon, label, checked, onCheckChange, }: { icon: ReactNode; label: ReactNode; checked: boolean; onCheckChange: (checked: boolean) => void; }) { const [isPressed, setIsPressed] = useState(false); return ( { setIsPressed(true); }} onPressOut={() => { setIsPressed(false); }} onPress={() => { onCheckChange(!checked); }} className={cn( "flex-1 items-start justify-center rounded-md px-3 py-2 gap-1", checked ? "border-2 border-primary" : "border border-border", { "bg-accent": isPressed }, )} > {icon} {label} ); } function TelemetrySection() { const lastMsgIdSent = useBleStore((state) => state.lastMsgIdSent); const lastPingAt = useBleStore((state) => state.lastPingAt); const lastDataAt = useBleStore((state) => state.lastDataAt); const lastNotifyAt = useBleStore((state) => state.lastNotifyAt); const notifyQueueDepth = useBleStore((state) => state.notifyQueueDepth); const droppedNotifyPackets = useBleStore( (state) => state.droppedNotifyPackets, ); const lastCommand = useBleStore((state) => state.lastCommand); return ( Telemetry {droppedNotifyPackets > 0 ? ( ) : null} {lastCommand ? ( ) : null} ); } function UuidsSection() { const uuids = Ble.getUuids(); return ( UUIDs {`SERVICE_UUID=${uuids.serviceUUID}\nFEED_TX_UUID=${uuids.feedTxUUID}\nCONTROL_RX_UUID=${uuids.controlRxUUID}\nWIFI_REQUEST_TX_UUID=${uuids.wifiRequestTxUUID}`} ); }