Spaces:
Running
Running
File size: 7,341 Bytes
c8b4583 540cfa6 c8b4583 540cfa6 c8b4583 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 |
/**
* lerobot.js Web Interface
*
* Browser-based interface for lerobot functionality
* Provides the same find-port functionality as the CLI but in the browser
*/
import "./web_interface.css";
import { findPortWeb } from "./lerobot/web/find_port.js";
document.querySelector<HTMLDivElement>("#app")!.innerHTML = `
<div class="lerobot-app">
<header class="lerobot-header">
<h1>π€ lerobot.js</h1>
<p>use your robot in the web & node with an api similar to LeRobot in python</p>
</header>
<main class="lerobot-main">
<section class="tool-section">
<h2>π Find USB Ports</h2>
<p>Identify which USB ports your robot arms are connected to</p>
<div class="button-group">
<button id="show-ports-btn" class="secondary-btn" style="display: none;">Show Available Ports</button>
<button id="manage-devices-btn" class="secondary-btn">Manage Devices</button>
<button id="find-port-btn" class="primary-btn">Find MotorsBus Port</button>
</div>
<div id="info-box" class="info-box" style="display: none;">
<span class="info-icon">π‘</span>
<span class="info-text">Use "Manage Devices" to pair additional devices or "Find MotorsBus Port" to start detection.</span>
</div>
<div id="port-results" class="results-area"></div>
</section>
<section class="info-section" id="compatibility-section" style="display: none;">
<h3>Browser Compatibility Issue</h3>
<p>Your browser doesn't support the <a href="https://web.dev/serial/" target="_blank">WebSerial API</a>. Please use:</p>
<ul>
<li>Chrome/Edge 89+ or Chrome Android 105+</li>
<li>HTTPS connection (or localhost for development)</li>
</ul>
<p>Alternatively, use the <strong>CLI version</strong>: <code>npx lerobot find-port</code></p>
</section>
</main>
</div>
`;
// Set up button functionality
const showPortsBtn =
document.querySelector<HTMLButtonElement>("#show-ports-btn")!;
const manageDevicesBtn = document.querySelector<HTMLButtonElement>(
"#manage-devices-btn"
)!;
const findPortBtn =
document.querySelector<HTMLButtonElement>("#find-port-btn")!;
const resultsArea = document.querySelector<HTMLDivElement>("#port-results")!;
const infoBox = document.querySelector<HTMLDivElement>("#info-box")!;
// Function to display paired devices
async function displayPairedDevices() {
try {
// Check WebSerial support
if (!("serial" in navigator)) {
resultsArea.innerHTML =
'<p class="error">WebSerial API not supported. Please use Chrome/Edge 89+ with HTTPS or localhost.</p>';
infoBox.style.display = "none";
showPortsBtn.style.display = "inline-block";
return;
}
// Check what ports we already have access to
const ports = await navigator.serial.getPorts();
if (ports.length > 0) {
// We have paired devices, show them
resultsArea.innerHTML = `<p class="success">Found ${ports.length} paired device(s):</p>`;
ports.forEach((port, index) => {
const info = port.getInfo();
if (info.usbVendorId && info.usbProductId) {
resultsArea.innerHTML += `<p class="log">Port ${index + 1}: USB:${
info.usbVendorId
}:${info.usbProductId}</p>`;
} else {
resultsArea.innerHTML += `<p class="log">Port ${
index + 1
}: Serial device</p>`;
}
});
// Show the info box with guidance
infoBox.style.display = "flex";
// Hide show ports button since we have devices
showPortsBtn.style.display = "none";
} else {
// No devices paired, show helpful message
resultsArea.innerHTML =
'<p class="log">No paired devices found. Click "Show Available Ports" to get started.</p>';
// Hide the info box since we don't have devices
infoBox.style.display = "none";
// Show the show ports button since we need it
showPortsBtn.style.display = "inline-block";
}
} catch (error) {
resultsArea.innerHTML += `<p class="error">Error checking devices: ${
error instanceof Error ? error.message : error
}</p>`;
infoBox.style.display = "none";
showPortsBtn.style.display = "inline-block";
}
}
// Check browser compatibility and show warning if needed
function checkBrowserCompatibility() {
const compatibilitySection = document.querySelector(
"#compatibility-section"
) as HTMLElement;
if (!("serial" in navigator)) {
// Browser doesn't support WebSerial API, show compatibility warning
compatibilitySection.style.display = "block";
} else {
// Browser supports WebSerial API, hide compatibility section
compatibilitySection.style.display = "none";
}
}
// Check for paired devices and browser compatibility on page load
checkBrowserCompatibility();
displayPairedDevices();
// Show available ports button (only for when no devices are paired)
showPortsBtn.addEventListener("click", async () => {
try {
showPortsBtn.disabled = true;
showPortsBtn.textContent = "Pairing devices...";
resultsArea.innerHTML =
'<p class="status">Requesting permission to access serial ports...</p>';
try {
await navigator.serial.requestPort();
// Refresh the display
await displayPairedDevices();
} catch (permissionError) {
console.log("Permission dialog cancelled:", permissionError);
}
} catch (error) {
resultsArea.innerHTML += `<p class="error">Error: ${
error instanceof Error ? error.message : error
}</p>`;
} finally {
showPortsBtn.disabled = false;
showPortsBtn.textContent = "Show Available Ports";
}
});
// Manage devices button (always available)
manageDevicesBtn.addEventListener("click", async () => {
try {
manageDevicesBtn.disabled = true;
manageDevicesBtn.textContent = "Managing...";
// Always show the permission dialog to pair new devices
try {
await navigator.serial.requestPort();
// Refresh the display to show updated device list
await displayPairedDevices();
resultsArea.innerHTML +=
'<p class="success">Device pairing completed. Updated device list above.</p>';
} catch (permissionError) {
console.log("Permission dialog cancelled:", permissionError);
}
} catch (error) {
resultsArea.innerHTML += `<p class="error">Error: ${
error instanceof Error ? error.message : error
}</p>`;
} finally {
manageDevicesBtn.disabled = false;
manageDevicesBtn.textContent = "Manage Devices";
resultsArea.scrollTop = resultsArea.scrollHeight;
}
});
// Find port button
findPortBtn.addEventListener("click", async () => {
try {
findPortBtn.disabled = true;
findPortBtn.textContent = "Finding ports...";
resultsArea.innerHTML = '<p class="status">Starting port detection...</p>';
await findPortWeb((message: string) => {
resultsArea.innerHTML += `<p class="log">${message}</p>`;
resultsArea.scrollTop = resultsArea.scrollHeight;
});
} catch (error) {
resultsArea.innerHTML += `<p class="error">Error: ${
error instanceof Error ? error.message : error
}</p>`;
} finally {
findPortBtn.disabled = false;
findPortBtn.textContent = "Find MotorsBus Port";
}
});
|