### Install smartcube-web-bluetooth via npm Source: https://github.com/poliva/smartcube-web-bluetooth/blob/main/README.md Install the library directly from the GitHub repository using npm. ```bash npm install poliva/smartcube-web-bluetooth ``` -------------------------------- ### Connect to a Smart Cube with Options Source: https://context7.com/poliva/smartcube-web-bluetooth/llms.txt Use `connectSmartCube` to establish a connection. Configure connection parameters like status callbacks, abort signals, and device selection modes. Handle connection errors and aborts gracefully. ```typescript import { connectSmartCube, SmartCubeConnection, SmartCubeEvent, ConnectSmartCubeOptions } from 'smartcube-web-bluetooth'; const abortController = new AbortController(); const options: ConnectSmartCubeOptions = { // Called with status messages during connection onStatus: (msg) => console.log('[Status]', msg), // Abort long-running steps (e.g. MAC candidate probing) signal: abortController.signal, // 'filtered' (default) uses name/manufacturer scan filters; 'any' accepts all BLE devices deviceSelection: 'filtered', // Enable slow MAC candidate search for QiYi / MoYu32 when advertisement hints fail enableAddressSearch: false, }; let conn: SmartCubeConnection; try { conn = await connectSmartCube(options); } catch (e) { if (e instanceof DOMException && e.name === 'AbortError') { console.log('Connection aborted by user'); } else { console.error('Connection failed:', e); } process.exit(1); } console.log('Connected to:', conn.deviceName); // e.g. "GAN12 ui" console.log('MAC address:', conn.deviceMAC); // e.g. "A1:B2:C3:D4:E5:F6" console.log('Protocol:', conn.protocol.name); // e.g. "GAN Gen2" console.log('Capabilities:', conn.capabilities); // { gyroscope: true, battery: true, facelets: true, hardware: true, reset: true } const subscription = conn.events$.subscribe((event: SmartCubeEvent) => { switch (event.type) { case 'MOVE': // event.move: string like "R", "U'", "F2" // event.face: 0=U, 1=R, 2=F, 3=D, 4=L, 5=B // event.direction: 0=CW, 1=CCW // event.localTimestamp: host clock ms (null if recovered missed move) // event.cubeTimestamp: cube internal clock ms (null if recovered missed move) console.log(`Move: ${event.move} at ${event.localTimestamp}ms`); break; case 'FACELETS': // event.facelets: 54-char Kociemba string, e.g. "UUUUUUUUURRRRRRRRRFFFFFFFFFDDDDDDDDDLLLLLLLLLBBBBBBBBB" console.log('Cube state:', event.facelets); break; case 'GYRO': // event.quaternion: { x, y, z, w } — Right-Handed, +X=Red, +Y=Blue, +Z=White // event.velocity?: { x, y, z } — angular velocity console.log('Orientation:', event.quaternion); break; case 'BATTERY': console.log(`Battery: ${event.batteryLevel}%`); break; case 'HARDWARE': console.log(`HW: ${event.hardwareName} v${event.hardwareVersion}, FW: ${event.softwareVersion}`); console.log(`Gyro supported: ${event.gyroSupported}`); break; case 'DISCONNECT': console.log('Cube disconnected'); subscription.unsubscribe(); break; } }); // Request current state if the cube supports it if (conn.capabilities.facelets) { await conn.sendCommand({ type: 'REQUEST_FACELETS' }); } if (conn.capabilities.battery) { await conn.sendCommand({ type: 'REQUEST_BATTERY' }); } if (conn.capabilities.hardware) { await conn.sendCommand({ type: 'REQUEST_HARDWARE' }); } // Optionally abort after 60 seconds setTimeout(() => abortController.abort(), 60_000); // Clean disconnect await conn.disconnect(); ``` -------------------------------- ### Add smartcube-web-bluetooth to package.json Source: https://github.com/poliva/smartcube-web-bluetooth/blob/main/README.md Include the library as a dependency in your project's package.json file. ```json { "dependencies": { "smartcube-web-bluetooth": "github:poliva/smartcube-web-bluetooth" } } ``` -------------------------------- ### Connect and Interact with SmartCube Source: https://context7.com/poliva/smartcube-web-bluetooth/llms.txt Establishes a connection to a SmartCube, retrieves device metadata and capabilities, sends commands, and subscribes to events. Ensure capabilities are checked before sending commands. ```typescript import { connectSmartCube, SmartCubeConnection, SmartCubeCapabilities } from 'smartcube-web-bluetooth'; const conn: SmartCubeConnection = await connectSmartCube(); // Read-only metadata const name: string = conn.deviceName; // Bluetooth advertised name const mac: string = conn.deviceMAC; // MAC address (may be empty string if unavailable) const proto = conn.protocol; // { id: string, name: string } // Feature flags — always check before sending commands const caps: SmartCubeCapabilities = conn.capabilities; // { // gyroscope: boolean, — cube streams GYRO events // battery: boolean, — REQUEST_BATTERY supported // facelets: boolean, — REQUEST_FACELETS supported // hardware: boolean, — REQUEST_HARDWARE supported // reset: boolean — REQUEST_RESET supported // } // Send commands (no-op / throws if unsupported) if (caps.reset) { await conn.sendCommand({ type: 'REQUEST_RESET' }); // resets cube's internal solved state } // events$ is a cold RxJS Observable; subscribe as many times as needed const sub = conn.events$.subscribe(e => console.log(e.type, e.timestamp)); // Disconnect gracefully (emits DISCONNECT event, then completes the Observable) await conn.disconnect(); sub.unsubscribe(); ``` -------------------------------- ### Connect with Legacy GAN-Cube and Custom MAC Provider Source: https://context7.com/poliva/smartcube-web-bluetooth/llms.txt Connects to legacy GAN-only cubes, supporting various protocol versions with AES encryption. Includes a custom MAC address provider for scenarios where automatic retrieval fails. Handles specific event types like MOVE, FACELETS, GYRO, and HARDWARE. ```typescript import { connectGanCube, GanCubeConnection, GanCubeEvent, GanCubeState, MacAddressProvider } from 'smartcube-web-bluetooth'; // Custom MAC address provider — called with isFallbackCall=false first, // then isFallbackCall=true if automatic retrieval also fails. const macProvider: MacAddressProvider = async (device, isFallbackCall) => { if (!isFallbackCall) return null; // let library try auto-detect first const input = prompt(`Enter MAC address for "${device.name}":`); return input || null; }; const conn: GanCubeConnection = await connectGanCube(macProvider); console.log('Device:', conn.deviceName, '/', conn.deviceMAC); conn.events$.subscribe((event: GanCubeEvent) => { if (event.type === 'MOVE') { // GAN-specific MOVE includes serial number console.log(`#${event.serial} ${event.move} face=${event.face} dir=${event.direction}`); console.log(` localTs=${event.localTimestamp} cubeTs=${event.cubeTimestamp}`); } else if (event.type === 'FACELETS') { // GAN-specific FACELETS includes full cubie state (CP/CO/EP/EO) const state: GanCubeState = event.state; console.log('Facelets:', event.facelets); console.log('Corner Permutation:', state.CP); console.log('Edge Orientation:', state.EO); } else if (event.type === 'GYRO') { console.log('Quaternion:', event.quaternion); // { x, y, z, w } console.log('Velocity:', event.velocity); // { x, y, z } — may be undefined } else if (event.type === 'HARDWARE') { console.log(`${event.hardwareName} HW:${event.hardwareVersion} SW:${event.softwareVersion}`); console.log(`Gyro: ${event.gyroSupported}, Produced: ${event.productDate ?? 'unknown'}`); } else if (event.type === 'DISCONNECT') { console.log('Disconnected'); } }); await conn.sendCubeCommand({ type: 'REQUEST_FACELETS' }); await conn.sendCubeCommand({ type: 'REQUEST_BATTERY' }); await conn.sendCubeCommand({ type: 'REQUEST_HARDWARE' }); await conn.sendCubeCommand({ type: 'REQUEST_RESET' }); // reset to solved await conn.disconnect(); ``` -------------------------------- ### connectSmartCube Source: https://context7.com/poliva/smartcube-web-bluetooth/llms.txt The primary entry point for connecting to any supported smart cube brand. It opens a browser Bluetooth device picker, performs GATT service discovery, resolves the correct brand-specific protocol driver, and returns a `SmartCubeConnection` with a typed event Observable. Options can be provided for MAC address providers, status callbacks, abort signals, and device selection mode. ```APIDOC ## `connectSmartCube(options?)` ### Description Connects to any supported smart cube brand by opening a browser Bluetooth device picker, discovering GATT services, and resolving the appropriate protocol driver. Returns a `SmartCubeConnection` object with a typed event Observable. ### Parameters #### Optional Parameters (`ConnectSmartCubeOptions`) - **onStatus** (function) - Callback function called with status messages during connection. - **signal** (AbortSignal) - An `AbortSignal` to abort long-running connection steps. - **deviceSelection** (string) - Mode for device selection. Defaults to 'filtered' (uses name/manufacturer scan filters). 'any' accepts all BLE devices. - **enableAddressSearch** (boolean) - Enables a slow MAC candidate search for specific cube brands when advertisement hints fail. Defaults to `false`. ### Returns - `SmartCubeConnection` - An object representing the connection to the smart cube, including device details and an event observable. ### Throws - `DOMException` with name 'AbortError' - If the connection attempt is aborted. - Other errors - If the connection fails for other reasons. ### Example ```typescript import { connectSmartCube, SmartCubeConnection, SmartCubeEvent, ConnectSmartCubeOptions } from 'smartcube-web-bluetooth'; const abortController = new AbortController(); const options: ConnectSmartCubeOptions = { onStatus: (msg) => console.log('[Status]', msg), signal: abortController.signal, deviceSelection: 'filtered', enableAddressSearch: false, }; let conn: SmartCubeConnection; try { conn = await connectSmartCube(options); console.log('Connected to:', conn.deviceName); console.log('MAC address:', conn.deviceMAC); console.log('Protocol:', conn.protocol.name); console.log('Capabilities:', conn.capabilities); const subscription = conn.events$.subscribe((event: SmartCubeEvent) => { // Handle different event types: MOVE, FACELETS, GYRO, BATTERY, HARDWARE, DISCONNECT switch (event.type) { case 'MOVE': console.log(`Move: ${event.move}`); break; case 'FACELETS': console.log('Cube state:', event.facelets); break; // ... other event types case 'DISCONNECT': console.log('Cube disconnected'); subscription.unsubscribe(); break; } }); // Request initial state if supported if (conn.capabilities.facelets) { await conn.sendCommand({ type: 'REQUEST_FACELETS' }); } // Optionally abort after a timeout setTimeout(() => abortController.abort(), 60_000); // Clean disconnect await conn.disconnect(); } catch (e) { if (e instanceof DOMException && e.name === 'AbortError') { console.log('Connection aborted by user'); } else { console.error('Connection failed:', e); } } ``` ``` -------------------------------- ### Connect to a GAN-specific smart cube (legacy) Source: https://github.com/poliva/smartcube-web-bluetooth/blob/main/README.md Use the legacy GAN-specific API for existing applications. This API connects to GAN cubes and provides events for facelets and moves. A command to request facelets can be sent. ```typescript import { connectGanCube } from 'smartcube-web-bluetooth'; var conn = await connectGanCube(); conn.events$.subscribe((event) => { if (event.type == "FACELETS") { console.log("Cube facelets state", event.facelets); } else if (event.type == "MOVE") { console.log("Cube move", event.move); } }); await conn.sendCubeCommand({ type: "REQUEST_FACELETS" }); ``` -------------------------------- ### Connect to any supported smart cube Source: https://github.com/poliva/smartcube-web-bluetooth/blob/main/README.md Use the generic Smart Cube API to connect to any supported smart cube. This API automatically detects the cube's brand and provides a unified event model. Subscribe to events like FACELETS, MOVE, GYRO, and BATTERY. Commands to request facelets or battery status can be sent if supported by the cube. ```typescript import { connectSmartCube, SmartCubeConnection, SmartCubeEvent } from 'smartcube-web-bluetooth'; // Connect to any supported smart cube const conn: SmartCubeConnection = await connectSmartCube(); conn.events$.subscribe((event: SmartCubeEvent) => { if (event.type === "FACELETS") { console.log("Cube facelets state", event.facelets); } else if (event.type === "MOVE") { console.log("Cube move", event.move, "face", event.face, "direction", event.direction); } else if (event.type === "GYRO") { console.log("Cube orientation quaternion", event.quaternion); } else if (event.type === "BATTERY") { console.log("Battery level", event.batteryLevel); } }); // Request current facelets / battery if supported if (conn.capabilities.facelets) { await conn.sendCommand({ type: "REQUEST_FACELETS" }); } if (conn.capabilities.battery) { await conn.sendCommand({ type: "REQUEST_BATTERY" }); } ``` -------------------------------- ### Connect to a GAN Smart Timer Source: https://github.com/poliva/smartcube-web-bluetooth/blob/main/README.md Connect to a GAN Smart Timer and subscribe to its state changes. The timer emits events for RUNNING and STOPPED states, including recorded time when stopped. Avoid polling `getRecordedTimes()` for the currently displayed time. ```typescript import { connectGanTimer, GanTimerState } from 'gan-web-bluetooth'; var conn = await connectGanTimer(); conn.events$.subscribe((timerEvent) => { switch (timerEvent.state) { case GanTimerState.RUNNING: console.log('Timer is started'); break; case GanTimerState.STOPPED: console.log(`Timer is stopped, recorded time = ${timerEvent.recordedTime}`); break; default: console.log(`Timer changed state to ${GanTimerState[timerEvent.state]}`); } }); ``` -------------------------------- ### Register a Custom Cube Protocol Source: https://context7.com/poliva/smartcube-web-bluetooth/llms.txt Implement the SmartCubeProtocol interface to define custom filters, services, matching logic, and connection procedures for a new cube brand. Use registerProtocol to add it to the library's registry. Verify registration using getRegisteredProtocols. ```typescript import { registerProtocol, getRegisteredProtocols, SmartCubeProtocol, SmartCubeConnection, AttachmentContext, MacAddressProvider } from 'smartcube-web-bluetooth'; // Implement a custom protocol for a hypothetical "MyCube" brand const myCubeProtocol: SmartCubeProtocol = { // BLE requestDevice scan filters nameFilters: [{ namePrefix: 'MyCube' }], // Additional GATT services to declare as optional in requestDevice optionalServices: ['0000abcd-0000-1000-8000-00805f9b34fb'], // Return true if this device's name matches this protocol matchesDevice(device: BluetoothDevice): boolean { return device.name?.startsWith('MyCube') ?? false; }, // Higher score wins when multiple protocols match the same GATT profile gattAffinity(serviceUuids: ReadonlySet, device: BluetoothDevice): number { return serviceUuids.has('0000abcd-0000-1000-8000-00805f9b34fb') ? 10 : 0; }, // Establish GATT connection and return a SmartCubeConnection async connect( device: BluetoothDevice, macProvider?: MacAddressProvider, context?: AttachmentContext ): Promise { context?.onStatus?.('Connecting to MyCube…'); const gatt = await device.gatt!.connect(); // ... GATT service setup ... throw new Error('MyCube protocol not fully implemented'); } }; registerProtocol(myCubeProtocol); // Verify registration const protocols = getRegisteredProtocols(); console.log('Registered protocols:', protocols.length); // All brand protocols + your custom one are now available to connectSmartCube() ``` -------------------------------- ### Connect to GAN Smart Timer Source: https://context7.com/poliva/smartcube-web-bluetooth/llms.txt Connects to a GAN Smart Timer or GAN Halo Smart Timer over BLE. Provides an RxJS Observable for timer events and a method to retrieve recorded times. Ensure proper disconnection after use. ```typescript import { connectGanTimer, GanTimerConnection, GanTimerEvent, GanTimerState, GanTimerTime, GanTimerRecordedTimes } from 'smartcube-web-bluetooth'; const conn: GanTimerConnection = await connectGanTimer(); conn.events$.subscribe((event: GanTimerEvent) => { switch (event.state) { case GanTimerState.IDLE: console.log('Timer idle / reset'); break; case GanTimerState.HANDS_ON: console.log('Hands placed on timer'); break; case GanTimerState.HANDS_OFF: console.log('Hands lifted before grace period expired'); break; case GanTimerState.GET_SET: console.log('Grace period expired — ready to start'); break; case GanTimerState.RUNNING: console.log('Timer running!'); break; case GanTimerState.STOPPED: // event.recordedTime is populated only in STOPPED state const t: GanTimerTime = event.recordedTime!; console.log(`Stopped: ${t.toString()}`); // e.g. "0:12.345" console.log(`As ms: ${t.asTimestamp}`); // e.g. 12345 console.log(`Parts: ${t.minutes}m ${t.seconds}s ${t.milliseconds}ms`); break; case GanTimerState.FINISHED: console.log('Timer finished, returning to IDLE'); break; case GanTimerState.DISCONNECT: console.log('Timer disconnected'); break; } }); // Read times stored in timer memory (do NOT poll this in a loop) // Returns displayTime (currently shown) + last 3 previousTimes const recorded: GanTimerRecordedTimes = await conn.getRecordedTimes(); console.log('Display time:', recorded.displayTime.toString()); recorded.previousTimes.forEach((t, i) => { console.log(`Previous ${i + 1}:`, t.toString()); }); // Cleanly disconnect conn.disconnect(); ``` -------------------------------- ### Smartcube Timer State Diagram Source: https://github.com/poliva/smartcube-web-bluetooth/blob/main/README.md Visual representation of the Smartcube timer's state transitions. Useful for understanding the flow of timer events. ```mermaid stateDiagram-v2 direction LR IDLE --> HANDS_ON HANDS_ON --> HANDS_OFF HANDS_OFF --> HANDS_ON HANDS_ON --> GET_SET GET_SET --> RUNNING RUNNING --> STOPPED STOPPED --> FINISHED FINISHED --> IDLE ``` -------------------------------- ### cubeTimestampLinearFit(cubeMoves) Source: https://context7.com/poliva/smartcube-web-bluetooth/llms.txt Corrects GAN Smart Cube clock skew using linear regression on a series of GanCubeMove objects. It handles missing timestamps by interpolating, then returns a new array of moves with corrected timestamps aligned to the host clock. ```APIDOC ## `cubeTimestampLinearFit(cubeMoves)` — Correct cube clock skew via linear regression GAN Smart Cube internal clocks drift relative to the host clock. This utility applies linear regression across a window of `GanCubeMove` objects to produce corrected `cubeTimestamp` values aligned to the host clock. Missed/recovered moves (where `cubeTimestamp` is `null`) are first gap-filled at ±50 ms intervals before regression. Returns a new array — does not mutate the input. ```typescript import { connectGanCube, GanCubeMove, cubeTimestampLinearFit, cubeTimestampCalcSkew } from 'smartcube-web-bluetooth'; const conn = await connectGanCube(); const moveWindow: GanCubeMove[] = []; const WINDOW_SIZE = 30; conn.events$.subscribe((event) => { if (event.type !== 'MOVE') return; moveWindow.push({ face: event.face, direction: event.direction, move: event.move, localTimestamp: event.localTimestamp, cubeTimestamp: event.cubeTimestamp, }); if (moveWindow.length > WINDOW_SIZE) { moveWindow.shift(); } if (moveWindow.length >= 2) { // Fitted moves have cubeTimestamp re-expressed in host-clock ms, // starting at 0 from the first move in the window. const fitted: GanCubeMove[] = cubeTimestampLinearFit([...moveWindow]); // Skew in percent: positive means cube clock runs fast, negative = slow const skewPercent: number = cubeTimestampCalcSkew(moveWindow); const lastFitted = fitted[fitted.length - 1]; console.log(`Move: ${event.move}`); console.log(` Raw cubeTs: ${event.cubeTimestamp} ms`); console.log(` Fitted cubeTs: ${lastFitted.cubeTimestamp} ms`); console.log(` Clock skew: ${skewPercent}%`); // Use fitted[i].cubeTimestamp as reliable elapsed-time values for solve timing } }); ``` ``` -------------------------------- ### SmartCubeConnection Interface Source: https://context7.com/poliva/smartcube-web-bluetooth/llms.txt The `SmartCubeConnection` interface provides read-only device metadata, a capabilities map, an RxJS event Observable, a command sender, and a disconnect method. This interface is consistent across all cube brands. ```APIDOC ## `SmartCubeConnection` — Connection interface The object returned by `connectSmartCube`. Provides read-only device metadata, a capabilities map, the RxJS event Observable, a command sender, and a disconnect method. All brands expose this same interface regardless of the underlying protocol. ```typescript import { connectSmartCube, SmartCubeConnection, SmartCubeCapabilities } from 'smartcube-web-bluetooth'; const conn: SmartCubeConnection = await connectSmartCube(); // Read-only metadata const name: string = conn.deviceName; // Bluetooth advertised name const mac: string = conn.deviceMAC; // MAC address (may be empty string if unavailable) const proto = conn.protocol; // { id: string, name: string } // Feature flags — always check before sending commands const caps: SmartCubeCapabilities = conn.capabilities; // { // gyroscope: boolean, — cube streams GYRO events // battery: boolean, — REQUEST_BATTERY supported // facelets: boolean, — REQUEST_FACELETS supported // hardware: boolean, — REQUEST_HARDWARE supported // reset: boolean — REQUEST_RESET supported // } // Send commands (no-op / throws if unsupported) if (caps.reset) { await conn.sendCommand({ type: 'REQUEST_RESET' }); // resets cube's internal solved state } // events$ is a cold RxJS Observable; subscribe as many times as needed const sub = conn.events$.subscribe(e => console.log(e.type, e.timestamp)); // Disconnect gracefully (emits DISCONNECT event, then completes the Observable) await conn.disconnect(); sub.unsubscribe(); ``` ``` -------------------------------- ### connectGanTimer() Source: https://context7.com/poliva/smartcube-web-bluetooth/llms.txt Connects to a GAN Smart Timer or GAN Halo Smart Timer over BLE. It returns a GanTimerConnection object which provides an RxJS Observable for GanTimerEvent objects and a method to retrieve stored timer data. ```APIDOC ## `connectGanTimer()` — Connect to GAN Smart Timer Connects to a GAN Smart Timer or GAN Halo Smart Timer over BLE. Returns a `GanTimerConnection` with an RxJS Observable of `GanTimerEvent` objects reflecting the timer's state machine, plus a method to read the last times stored in timer memory. ```typescript import { connectGanTimer, GanTimerConnection, GanTimerEvent, GanTimerState, GanTimerTime, GanTimerRecordedTimes } from 'smartcube-web-bluetooth'; const conn: GanTimerConnection = await connectGanTimer(); conn.events$.subscribe((event: GanTimerEvent) => { switch (event.state) { case GanTimerState.IDLE: console.log('Timer idle / reset'); break; case GanTimerState.HANDS_ON: console.log('Hands placed on timer'); break; case GanTimerState.HANDS_OFF: console.log('Hands lifted before grace period expired'); break; case GanTimerState.GET_SET: console.log('Grace period expired — ready to start'); break; case GanTimerState.RUNNING: console.log('Timer running!'); break; case GanTimerState.STOPPED: // event.recordedTime is populated only in STOPPED state const t: GanTimerTime = event.recordedTime!; console.log(`Stopped: ${t.toString()}`); // e.g. "0:12.345" console.log(`As ms: ${t.asTimestamp}`); // e.g. 12345 console.log(`Parts: ${t.minutes}m ${t.seconds}s ${t.milliseconds}ms`); break; case GanTimerState.FINISHED: console.log('Timer finished, returning to IDLE'); break; case GanTimerState.DISCONNECT: console.log('Timer disconnected'); break; } }); // Read times stored in timer memory (do NOT poll this in a loop) // Returns displayTime (currently shown) + last 3 previousTimes const recorded: GanTimerRecordedTimes = await conn.getRecordedTimes(); console.log('Display time:', recorded.displayTime.toString()); recorded.previousTimes.forEach((t, i) => { console.log(`Previous ${i + 1}:`, t.toString()); }); // Cleanly disconnect conn.disconnect(); ``` ``` -------------------------------- ### Convert Cube State to Kociemba Facelets String Source: https://context7.com/poliva/smartcube-web-bluetooth/llms.txt Use this function to convert a cube's internal state (corner/edge permutation and orientation) into the standard 54-character Kociemba facelet string. This is essential for integrating with external solvers like min2phase or kociemba. The face order is U R F D L B. ```typescript import { toKociembaFacelets } from 'smartcube-web-bluetooth'; // Solved state const solvedFacelets = toKociembaFacelets( [0, 1, 2, 3, 4, 5, 6, 7], // cp — corner permutation [0, 0, 0, 0, 0, 0, 0, 0], // co — corner orientation [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], // ep — edge permutation [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] // eo — edge orientation ); console.log(solvedFacelets); // → "UUUUUUUUURRRRRRRRRFFFFFFFFFDDDDDDDDDLLLLLLLLLBBBBBBBBB" ``` ```typescript // State after F R moves const scrambledFacelets = toKociembaFacelets( [0, 5, 2, 1, 7, 4, 6, 3], [1, 2, 0, 2, 1, 1, 0, 2], [1, 9, 2, 3, 11, 8, 6, 7, 4, 5, 10, 0], [1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0] ); console.log(scrambledFacelets); // → "UUFUUFLLFUUURRRRRRFFRFFDFFDRRBDDBDDBLLDLLDLLDLBBUBBUBB" ``` ```typescript // Use live from a GAN cube connection import { connectGanCube } from 'smartcube-web-bluetooth'; const conn = await connectGanCube(); conn.events$.subscribe(event => { if (event.type === 'FACELETS') { // event.facelets is already the Kociemba string — computed internally via this function console.log('Current state for solver:', event.facelets); } }); ``` -------------------------------- ### toKociembaFacelets(cp, co, ep, eo) Source: https://context7.com/poliva/smartcube-web-bluetooth/llms.txt Converts a cube state represented by corner and edge permutation and orientation arrays into the standard 54-character Kociemba facelet string. This is useful for integrating with cube solving algorithms. ```APIDOC ## `toKociembaFacelets(cp, co, ep, eo)` — Convert cubie state to Kociemba facelet string Converts a cube state expressed as Corner/Edge Permutation and Orientation arrays into the standard 54-character Kociemba facelet string (face order: U R F D L B). Useful for integrating with solvers such as `min2phase` or `kociemba`. ### Parameters #### Path Parameters - **cp** (array) - Corner permutation - **co** (array) - Corner orientation - **ep** (array) - Edge permutation - **eo** (array) - Edge orientation ### Request Example ```typescript import { toKociembaFacelets } from 'smartcube-web-bluetooth'; // Solved state const solvedFacelets = toKociembaFacelets( [0, 1, 2, 3, 4, 5, 6, 7], // cp — corner permutation [0, 0, 0, 0, 0, 0, 0, 0], // co — corner orientation [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], // ep — edge permutation [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] // eo — edge orientation ); console.log(solvedFacelets); // → "UUUUUUUUURRRRRRRRRFFFFFFFFFDDDDDDDDDLLLLLLLLLBBBBBBBBB" // State after F R moves const scrambledFacelets = toKociembaFacelets( [0, 5, 2, 1, 7, 4, 6, 3], [1, 2, 0, 2, 1, 1, 0, 2], [1, 9, 2, 3, 11, 8, 6, 7, 4, 5, 10, 0], [1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0] ); console.log(scrambledFacelets); // → "UUFUUFLLFUUURRRRRRFFRFFDFFDRRBDDBDDBLLDLLDLLDLBBUBBUBB" ``` ### Response #### Success Response (200) - **facelets** (string) - The 54-character Kociemba facelet string. ``` -------------------------------- ### connectGanCube - Legacy GAN-only cube connection Source: https://context7.com/poliva/smartcube-web-bluetooth/llms.txt The `connectGanCube` function is the original GAN-specific connection method for GAN Gen1–Gen4 devices. It supports various GAN cube models and handles different protocol versions, including AES encryption. ```APIDOC ## `connectGanCube(macAddressProvider?)` — Legacy GAN-only cube connection (GAN Gen1–Gen4) The original GAN-specific connection function. Targets GAN, MG, and AiCube prefixed devices. Handles Gen1 (unencrypted), Gen2, Gen3, and Gen4 protocols with AES encryption keyed from the device's MAC address. Accepts an optional async callback to supply the MAC address when automatic BLE advertisement retrieval is unavailable (e.g., non-Chromium browsers). ```typescript import { connectGanCube, GanCubeConnection, GanCubeEvent, GanCubeState, MacAddressProvider } from 'smartcube-web-bluetooth'; // Custom MAC address provider — called with isFallbackCall=false first, // then isFallbackCall=true if automatic retrieval also fails. const macProvider: MacAddressProvider = async (device, isFallbackCall) => { if (!isFallbackCall) return null; // let library try auto-detect first const input = prompt(`Enter MAC address for "${device.name}":`); return input || null; }; const conn: GanCubeConnection = await connectGanCube(macProvider); console.log('Device:', conn.deviceName, '/', conn.deviceMAC); conn.events$.subscribe((event: GanCubeEvent) => { if (event.type === 'MOVE') { // GAN-specific MOVE includes serial number console.log(`#${event.serial} ${event.move} face=${event.face} dir=${event.direction}`); console.log(` localTs=${event.localTimestamp} cubeTs=${event.cubeTimestamp}`); } else if (event.type === 'FACELETS') { // GAN-specific FACELETS includes full cubie state (CP/CO/EP/EO) const state: GanCubeState = event.state; console.log('Facelets:', event.facelets); console.log('Corner Permutation:', state.CP); console.log('Edge Orientation:', state.EO); } else if (event.type === 'GYRO') { console.log('Quaternion:', event.quaternion); // { x, y, z, w } console.log('Velocity:', event.velocity); // { x, y, z } — may be undefined } else if (event.type === 'HARDWARE') { console.log(`${event.hardwareName} HW:${event.hardwareVersion} SW:${event.softwareVersion}`); console.log(`Gyro: ${event.gyroSupported}, Produced: ${event.productDate ?? 'unknown'}`); } else if (event.type === 'DISCONNECT') { console.log('Disconnected'); } }); await conn.sendCubeCommand({ type: 'REQUEST_FACELETS' }); await conn.sendCubeCommand({ type: 'REQUEST_BATTERY' }); await conn.sendCubeCommand({ type: 'REQUEST_HARDWARE' }); await conn.sendCubeCommand({ type: 'REQUEST_RESET' }); // reset to solved await conn.disconnect(); ``` ``` -------------------------------- ### Correct GAN Smart Cube Clock Skew Source: https://context7.com/poliva/smartcube-web-bluetooth/llms.txt Applies linear regression to GAN Smart Cube move data to correct for internal clock drift relative to the host clock. Handles missed moves by gap-filling before regression. Returns a new array without mutating the input. ```typescript import { connectGanCube, GanCubeMove, cubeTimestampLinearFit, cubeTimestampCalcSkew } from 'smartcube-web-bluetooth'; const conn = await connectGanCube(); const moveWindow: GanCubeMove[] = []; const WINDOW_SIZE = 30; conn.events$.subscribe((event) => { if (event.type !== 'MOVE') return; moveWindow.push({ face: event.face, direction: event.direction, move: event.move, localTimestamp: event.localTimestamp, cubeTimestamp: event.cubeTimestamp, }); if (moveWindow.length > WINDOW_SIZE) { moveWindow.shift(); } if (moveWindow.length >= 2) { // Fitted moves have cubeTimestamp re-expressed in host-clock ms, // starting at 0 from the first move in the window. const fitted: GanCubeMove[] = cubeTimestampLinearFit([...moveWindow]); // Skew in percent: positive means cube clock runs fast, negative = slow const skewPercent: number = cubeTimestampCalcSkew(moveWindow); const lastFitted = fitted[fitted.length - 1]; console.log(`Move: ${event.move}`); console.log(` Raw cubeTs: ${event.cubeTimestamp} ms`); console.log(` Fitted cubeTs: ${lastFitted.cubeTimestamp} ms`); console.log(` Clock skew: ${skewPercent}%`); // Use fitted[i].cubeTimestamp as reliable elapsed-time values for solve timing } }); ``` -------------------------------- ### Read recorded times from GAN Smart Timer Source: https://github.com/poliva/smartcube-web-bluetooth/blob/main/README.md Retrieve recorded times from the GAN Smart Timer's memory. This includes the currently displayed time and a list of previous times. Note that this method is not intended for polling the currently displayed time. ```typescript var recTimes = await conn.getRecordedTimes(); console.log(`Time on display = ${recTimes.displayTime}`); recTimes.previousTimes.forEach((pt, i) => console.log(`Previous time ${i} = ${pt}`)); ``` -------------------------------- ### getCachedMacForDevice(device) / removeCachedMacForDevice(device) Source: https://context7.com/poliva/smartcube-web-bluetooth/llms.txt Manages the caching of a device's MAC address in localStorage. After a successful connection, the MAC is cached using the BluetoothDevice.id as the key, which speeds up subsequent reconnections by avoiding BLE advertisement scans. ```APIDOC ## `getCachedMacForDevice(device)` / `removeCachedMacForDevice(device)` — MAC address localStorage cache After a successful verified connection, the library caches the device's MAC address in `localStorage` keyed by the browser's opaque `BluetoothDevice.id`. On reconnect, this avoids the slow BLE advertisement scan needed to recover the MAC. You can read or evict these cache entries manually. ### Parameters #### Path Parameters - **device** (BluetoothDevice) - The Bluetooth device object. ### Request Example ```typescript import { connectSmartCube, getCachedMacForDevice, removeCachedMacForDevice } from 'smartcube-web-bluetooth'; // First connection — MAC is resolved from advertisements and cached automatically const conn = await connectSmartCube({ onStatus: s => console.log(s) }); console.log('Connected, MAC:', conn.deviceMAC); // Inspect the cache (useful for debugging) // NOTE: BluetoothDevice is not available outside a connection flow in most browsers, // so cache inspection is typically done during or right after connection. // The device object is internal — demonstrate pattern via re-connection check: conn.events$.subscribe(async (event) => { if (event.type === 'DISCONNECT') { console.log('Cube disconnected. Cached MAC will be used on next connect.'); } }); // If a connection keeps timing out with a wrong MAC (e.g. after cube factory reset), // clear the stale cache entry and retry: async function reconnectWithFreshMac() { try { return await connectSmartCube({ onStatus: s => console.log(s) }); } catch (e) { console.warn('Connection failed, will clear MAC cache on next attempt.'); // removeCachedMacForDevice is called internally on timeout errors; // you can also call it directly if you hold a reference to the BluetoothDevice. throw e; } } ``` ### Response #### Success Response (200) - **getCachedMacForDevice**: Returns the cached MAC address string if found, otherwise null. - **removeCachedMacForDevice**: No return value, but removes the cached MAC address for the given device. ``` -------------------------------- ### Manage Device MAC Address Cache Source: https://context7.com/poliva/smartcube-web-bluetooth/llms.txt This utility caches a device's MAC address in localStorage, keyed by `BluetoothDevice.id`, to avoid slow BLE scans on subsequent connections. You can manually retrieve or remove cached entries. This is particularly useful for debugging connection issues or after a cube factory reset. ```typescript import { connectSmartCube, getCachedMacForDevice, removeCachedMacForDevice } from 'smartcube-web-bluetooth'; // First connection — MAC is resolved from advertisements and cached automatically const conn = await connectSmartCube({ onStatus: s => console.log(s) }); console.log('Connected, MAC:', conn.deviceMAC); ``` ```typescript // Inspect the cache (useful for debugging) // NOTE: BluetoothDevice is not available outside a connection flow in most browsers, // so cache inspection is typically done during or right after connection. // The device object is internal — demonstrate pattern via re-connection check: conn.events$.subscribe(async (event) => { if (event.type === 'DISCONNECT') { console.log('Cube disconnected. Cached MAC will be used on next connect.'); } }); ``` ```typescript // If a connection keeps timing out with a wrong MAC (e.g. after cube factory reset), // clear the stale cache entry and retry: async function reconnectWithFreshMac() { try { return await connectSmartCube({ onStatus: s => console.log(s) }); } catch (e) { console.warn('Connection failed, will clear MAC cache on next attempt.'); // removeCachedMacForDevice is called internally on timeout errors; // you can also call it directly if you hold a reference to the BluetoothDevice. throw e; } } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.