"use client" import { useState, useEffect, useMemo } from "react" import { Power, PowerOff, Keyboard } from "lucide-react" import { Button } from "@/components/ui/button" import { Card } from "@/components/ui/card" import { Badge } from "@/components/ui/badge" import { Slider } from "@/components/ui/slider" import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip" import { cn } from "@/lib/utils" import { lerobot } from "@/lib/mock-api" import { useLocalStorage } from "@/hooks/use-local-storage" import VirtualKey from "@/components/VirtualKey" import type { RobotConnection, WebCalibrationResults, TeleoperationState } from "@/types/robot" interface TeleoperationViewProps { robot: RobotConnection } export function TeleoperationView({ robot }: TeleoperationViewProps) { const [localCalibrationData] = useLocalStorage(`calibration-${robot.robotId}`, null) const [teleopState, setTeleopState] = useState(null) const [teleopProcess, setTeleopProcess] = useState<{ start: () => void stop: () => void updateKeyState: (key: string, pressed: boolean) => void moveMotor: (motorName: string, position: number) => void } | null>(null) const calibrationData = useMemo(() => { if (localCalibrationData) return localCalibrationData return lerobot.MOCK_MOTOR_NAMES.reduce((acc, name) => { acc[name] = { min: 1000, max: 3000 } return acc }, {} as WebCalibrationResults) }, [localCalibrationData]) useEffect(() => { let process: Awaited> const setup = async () => { process = await lerobot.teleoperate(robot, { calibrationData, onStateUpdate: setTeleopState, }) setTeleopProcess(process) } setup() const handleKeyDown = (e: KeyboardEvent) => process?.updateKeyState(e.key, true) const handleKeyUp = (e: KeyboardEvent) => process?.updateKeyState(e.key, false) window.addEventListener("keydown", handleKeyDown) window.addEventListener("keyup", handleKeyUp) return () => { process?.stop() window.removeEventListener("keydown", handleKeyDown) window.removeEventListener("keyup", handleKeyUp) } }, [robot, calibrationData]) const motorConfigs = teleopState?.motorConfigs ?? lerobot.MOCK_MOTOR_NAMES.map((name) => ({ name, currentPosition: 2048, minPosition: calibrationData[name]?.min ?? 0, maxPosition: calibrationData[name]?.max ?? 4095, })) const keyStates = teleopState?.keyStates ?? {} const controls = lerobot.SO100_KEYBOARD_CONTROLS return (

robot control

manual teleoperate interface

{teleopState?.isActive ? ( ) : ( )}
status: {teleopState?.isActive ? "ACTIVE" : "STOPPED"}

Motor Control

{motorConfigs.map((motor) => (
teleopProcess?.moveMotor(motor.name, val[0])} disabled={!teleopState?.isActive} /> {Math.round(motor.currentPosition)}
))}

Keyboard Layout & Status

Shoulder
Elbow/Wrist
Roll/Grip
Active Keys: {Object.values(keyStates).filter((k) => k.pressed).length}
ESC
Emergency Stop
) }