### Implementing a Background Service with PluginServiceFactory Source: https://context7.com/roboninecom/plugin-sdk/llms.txt This example shows how to define a background service using `PluginServiceFactory`. The factory function is called once on app start and its return value is registered and accessible by other plugins via `context.service()`. Ensure the service exports a `PluginService` factory. ```ts // src/service.ts import type { PluginServiceContext, PluginServiceFactory } from '@robonine/plugin-sdk' interface TelemetryService { getLatestJoints(): Promise | null> startRecording(): void stopRecording(): void } export const PluginService: PluginServiceFactory = (ctx: PluginServiceContext): TelemetryService => { let recording = false let latest: Record | null = null // ctx.user, ctx.locale, ctx.service(), ctx.listUserRobots(), ctx.listUserPaths(), // ctx.readPath(), ctx.listConnectedRobots(), ctx.getRobotPosition(), // ctx.stopRobot(), ctx.moveToPosition(), ctx.goHome(), ctx.executePath() async function poll() { if (!recording) return const pos = await ctx.getRobotPosition('default') if (pos) latest = pos.joints setTimeout(poll, 100) } return { getLatestJoints: async () => latest, startRecording: () => { recording = true; poll() }, stopRecording: () => { recording = false }, } } ``` -------------------------------- ### Plugin Context API Example Source: https://github.com/roboninecom/plugin-sdk/blob/master/README.md Illustrates the available properties and methods within the PluginContext, including locale, connection status, UI primitives, and robot control APIs. Note that robot APIs require matching scopes. ```typescript context.locale // active locale string ('en', 'ru', …) context.connection // { connected: boolean } context.openConnectDialog() // open the robot connection dialog context.showSafetyWarning() // show the standard safety prompt (required before any motion) // robot APIs (require matching scope) context.servo.setPosition(id, value) context.servo.syncSetPositions([{ id, position }, …]) context.robotConfig // joint→servo mapping, encoder helpers // UI primitives (platform-styled) context.ui.Button context.ui.Input context.ui.Slider // 3D visualisation context.WorldView // ref-forwarded React component ``` -------------------------------- ### Using Platform-Styled UI Components in a Plugin Source: https://context7.com/roboninecom/plugin-sdk/llms.txt This example demonstrates how to import and use UI components provided by the Robonine platform within a plugin. Ensure all necessary UI components are destructured from `context.ui`. ```tsx import type { PluginContext } from '@robonine/plugin-sdk' import { useRef, useState } from 'react' export function PluginRoot({ context }: { context: PluginContext }) { const { Button, Card, Input, Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription, Separator, Skeleton, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Tooltip, TooltipContent, TooltipProvider, } = context.ui const [speed, setSpeed] = useState(512) const [open, setOpen] = useState(false) return ( Joint Position shoulder_pan
setSpeed(Number(e.target.value))} placeholder="Speed (0–1023)" /> Immediately disables all servo torque Confirm Stop All servos will go limp immediately.
) } ``` -------------------------------- ### Plugin Manifest Definition (Sample) Source: https://github.com/roboninecom/plugin-sdk/blob/master/README.md Example of a minimal UI-only plugin manifest. It declares no hardware scopes, making it suitable for interface-only functionalities. ```typescript import type { PluginManifest } from '@robonine/plugin-sdk' export const manifest: PluginManifest = { sdkVersion: '1', vendor: 'your-name', slug: 'hello-robot', name: { en: 'Hello robot', ru: 'Привет, робот' }, description: { en: 'A minimal example plugin that greets your robot.', ru: 'Минимальный пример плагина, который приветствует вашего робота.', }, icon: 'Bot', scopes: [], } ``` -------------------------------- ### Create Plugin Root Component in React Source: https://github.com/roboninecom/plugin-sdk/blob/master/README.md Implement the main component for your plugin using React and TypeScript. This example shows how to handle translations based on the current locale and conditionally render UI elements for connection status. ```typescript import type { PluginContext } from '@robonine/plugin-sdk' import { translations } from './translations' import { useMemo } from 'react' export function PluginRoot({ context }: { context: PluginContext }) { const t = useMemo( () => translations[context.locale as keyof typeof translations] ?? translations.en, [context.locale], ) if (!context.connection.connected) { return (

{t.title}

{t.connectPrompt}

{t.connectButton}
) } return (

{t.title}

{t.greeting}

) } ``` -------------------------------- ### Consuming a Background Service in a Plugin Source: https://context7.com/roboninecom/plugin-sdk/llms.txt This example demonstrates how a plugin can consume a background service registered with `PluginServiceFactory`. Use `context.service(providerId)` to retrieve the service instance and cast it to the expected interface. Handle cases where the service might not be available. ```tsx // Consuming plugin — src/plugin.tsx import type { PluginContext } from '@robonine/plugin-sdk' export function PluginRoot({ context }: { context: PluginContext }) { async function fetchLatest() { const svc = context.service('acme/arm-telemetry') as { getLatestJoints(): Promise | null> } | null if (!svc) { context.toast.error('Telemetry service not running'); return } const joints = await svc.getLatestJoints() console.log('Latest joints:', joints) } return } ``` -------------------------------- ### Plugin Index File Exports in TypeScript Source: https://context7.com/roboninecom/plugin-sdk/llms.txt Defines the required and optional exports for a Robonine plugin, including the manifest, the main plugin component, and optionally a service for installable plugins. ```typescript // src/index.ts — required exports export { manifest } from './manifest' // named export for tooling export { PluginRoot } from './plugin' // required by platform loader export { manifest as default } from './manifest' // default export // Optional — only for installable plugins with 'install' scope: export { PluginService } from './service' ``` -------------------------------- ### Compute Forward and Inverse Kinematics Source: https://context7.com/roboninecom/plugin-sdk/llms.txt Use `context.kinematics.forwardKinematics` for FK calculations. IK requires a trained model and may return null if unavailable. Ensure the robot configuration includes `ikModelUrl` for IK functionality. ```tsx import type { PluginContext } from '@robonine/plugin-sdk' import { useState } from 'react' export function PluginRoot({ context }: { context: PluginContext }) { const [pos, setPos] = useState<[number, number, number] | null>(null) async function computeFK() { const result = await context.kinematics.forwardKinematics({ shoulder_pan: 0.0, shoulder_lift: -0.5, elbow_flex: 1.2, wrist_flex: 0.0, }) // result.position → [x, y, z] in metres // result.rotation → 3×3 row-major rotation matrix setPos(result.position) console.log('End-effector:', result.position, result.rotation) } async function computeIK() { const targetXYZ: [number, number, number] = [0.15, 0.05, 0.30] const seed = { shoulder_pan: 0, shoulder_lift: -0.3, elbow_flex: 0.8, wrist_flex: 0 } const angles = await context.kinematics.inverseKinematics(targetXYZ, seed) if (!angles) { context.toast.error('IK model unavailable or no solution found') return } // Move to the solved angles await context.servo.setJointPositions(Object.values(angles)) context.toast.success('Moved to target position') } return (
{pos &&

EE: {pos.map(v => v.toFixed(3)).join(', ')}

}
) } ``` -------------------------------- ### Plugin Manifest Definition Source: https://github.com/roboninecom/plugin-sdk/blob/master/README.md Define your plugin's metadata, including vendor, slug, name, description, icon, and required hardware scopes. Ensure vendor and slug are URL-safe and lowercase. ```typescript import type { PluginManifest } from '@robonine/plugin-sdk' export const manifest: PluginManifest = { sdkVersion: '1', vendor: 'your-name', // lowercase, URL-safe — your unique namespace slug: 'my-plugin', // lowercase, URL-safe — unique within your namespace name: { en: 'My plugin', ru: 'Мой плагин' }, description: { en: 'What it does.', ru: 'Что делает.' }, icon: 'Wrench', // Lucide icon name or inline SVG string scopes: ['robot.control'], // capabilities the plugin needs (see Scopes below) } ``` -------------------------------- ### Export Plugin Components and Manifest Source: https://github.com/roboninecom/plugin-sdk/blob/master/README.md Export the main plugin components and the plugin manifest from your plugin's entry point. This ensures that the platform can correctly load and recognize your plugin. ```typescript export { manifest } from './manifest' export { PluginRoot } from './plugin' export { manifest as default } from './manifest' ``` -------------------------------- ### Controlling Robot Servos with RobotHandle Source: https://context7.com/roboninecom/plugin-sdk/llms.txt Shows how to use `RobotHandle` to manage robot connections and control individual servos. Includes opening connection dialogs, confirming safety warnings, setting servo positions, syncing movements, and reading telemetry. ```tsx import type { PluginContext } from '@robonine/plugin-sdk' import { useEffect, useState } from 'react' export function PluginRoot({ context }: { context: PluginContext }) { const defaultArm = context.robot('default') const leaderArm = context.robot('leader') // requires robot.leader scope const { connected, robotId, robotModel, virtual, remote } = defaultArm.connection async function handleConnect() { defaultArm.openConnectDialog() } async function handleSafetyAndMove() { const confirmed = await defaultArm.showSafetyWarning() if (!confirmed) return // Move servo ID 1 to raw encoder position 2048 at half speed await defaultArm.servo.setPosition(1, 2048, 512) // Sync-move multiple servos in one bus write await defaultArm.servo.syncSetPositions([ { id: 1, position: 2048 }, { id: 2, position: 1024 }, { id: 3, position: 3000 }, ]) // Move by URDF joint values (rad / m), ordered by servo ID ascending await defaultArm.servo.setJointPositions([0, -0.5, 1.2, 0, 0, 0]) // Speed control await defaultArm.servo.limitSpeed(300) // limit all servos; 0 = max speed // Emergency stop registration (call on mount, cleanup on unmount) const cleanup = defaultArm.servo.registerEmergencyStop() return cleanup } async function readTelemetry() { const raw = await defaultArm.servo.readPosition(1) // 0–4095 const regs = await defaultArm.servo.readRegisters(1, 0x24, 2) // addr, len const joints = await defaultArm.servo.readJointPositions() // number[] | null console.log({ raw, regs, joints }) } return connected ? : } ``` -------------------------------- ### Using PluginContext in a PluginRoot Component Source: https://context7.com/roboninecom/plugin-sdk/llms.txt Demonstrates how to access and use various features of the `PluginContext` within a React component. Includes locale, localization, user data, navigation, API calls, toasts, camera access, and service communication. ```tsx import type { PluginContext } from '@robonine/plugin-sdk' export function PluginRoot({ context }: { context: PluginContext }) { // Locale const locale = context.locale // 'en' | 'ru' | ... const label = context.localize({ en: 'Go', ru: 'Вперёд' }) // auto-picks locale // User const user = context.user // PluginUser | null if (user) console.log(user.email, user.role) // Navigation & API context.navigate('/tools') const res = await context.apiFetch('/api/robots') // auth headers injected // Toast context.toast.success('Motion complete') context.toast.error('Connection lost') // Cameras (requires camera.read scope) for (const cam of context.cameras) { console.log(cam.id, cam.label, cam.source) // 'local' | 'remote' } // Service inter-plugin communication (requires install scope on provider) const recorderApi = context.service('acme/arm-telemetry') return
...
} ``` -------------------------------- ### Kinematics API Source: https://context7.com/roboninecom/plugin-sdk/llms.txt Provides methods for forward and inverse kinematics calculations. Forward kinematics is always available, while inverse kinematics requires a trained model. ```APIDOC ## Kinematics API `context.kinematics` is always available. ### forwardKinematics Calculates the forward kinematics for a given set of joint angles. - **shoulder_pan** (number) - The shoulder pan angle. - **shoulder_lift** (number) - The shoulder lift angle. - **elbow_flex** (number) - The elbow flex angle. - **wrist_flex** (number) - The wrist flex angle. Returns an object containing `position` ([x, y, z] in metres) and `rotation` (3x3 row-major rotation matrix). ### inverseKinematics Calculates the inverse kinematics for a target position. - **targetXYZ** ([number, number, number]) - The target end-effector position in metres. - **seed** (object) - An object containing initial joint angles for the IK solver. - **shoulder_pan** (number) - **shoulder_lift** (number) - **elbow_flex** (number) - **wrist_flex** (number) Returns an object containing the solved joint angles, or `null` if the IK model is unavailable or no solution is found. ``` -------------------------------- ### Declare Plugin Metadata and Scopes with PluginManifest Source: https://context7.com/roboninecom/plugin-sdk/llms.txt Export a `PluginManifest` object to define your plugin's identity, display information, icon, and required capabilities (scopes). Ensure `sdkVersion` is '1', and `vendor` and `slug` are unique and URL-safe. Request only the scopes your plugin actually needs. ```typescript import type { PluginManifest } from '@robonine/plugin-sdk' export const manifest: PluginManifest = { sdkVersion: '1', vendor: 'acme', slug: 'arm-telemetry', name: { en: 'Arm Telemetry', ru: 'Телеметрия руки', }, description: { en: 'Live joint-position readout and motion recorder for a single robot arm.', ru: 'Отображение позиций суставов и запись движений одного манипулятора.', }, icon: 'Activity', scopes: ['robot.read'], provides: 'acme/arm-telemetry', dependencies: [], } ``` -------------------------------- ### Interact with Robot Visualization using WorldView Source: https://context7.com/roboninecom/plugin-sdk/llms.txt Use `context.WorldView` as a ref-forwarded React component for 3D robot visualization. Call imperative methods via a `useRef` to control joints, links, and target positions. The host injects robot configuration automatically. ```tsx import type { PluginContext, WorldViewApi } from '@robonine/plugin-sdk' import { useRef } from 'react' export function PluginRoot({ context }: { context: PluginContext }) { const viewRef = useRef(null) function highlight() { const api = viewRef.current if (!api) return // Read available joints const joints = api.getJoints() console.log(joints.map(j => `${j.name} (${j.type}): [${j.lower}, ${j.upper}]`)) // Drive individual joints (URDF rad / m) api.setJoint('shoulder_pan', 0.5) api.setJoint('elbow_flex', -1.0) // Highlight links with foreground / background colors (hex as number) api.setLinkHighlight(['upper_arm_link', 'forearm_link'], 0xff6600, 0x333333) // Place the IK target sphere api.setTargetPosition([0.15, 0.05, 0.30]) // Query world positions of joints const pos = api.getJointWorldPosition('wrist_flex') // Vector3 | null const axis = api.getJointWorldAxis('wrist_flex') // Reset all joints smoothly api.resetJoints(true) api.setRobotOpacity(0.8) } return (
console.log('Loaded', joints.length, 'joints')} onLoadProgress={(loaded, total) => console.log(`${loaded}/${total}`)} onTargetMove={(pos) => console.log('Target:', pos)} />
) } ``` -------------------------------- ### Servo Calibration API Source: https://context7.com/roboninecom/plugin-sdk/llms.txt Methods for calibrating servo motors, including writing homing offsets and range data to EEPROM. These operations require the `robot.calibration` scope and persist data to the backend. ```APIDOC ## Servo calibration API — Write EEPROM homing offsets and range data Calibration methods live on `RobotHandle.servo` and require the `robot.calibration` scope. `saveCalibration` and `saveRangeCalibration` persist data to the backend; they are top-level methods on `RobotHandle`. ### `context.servo.disableTorque()` Disables torque on the servo motors, allowing for physical manipulation of the robot arm. ### `context.servo.calibrateNeutralPositions(positions: Array<{ id: number, neutral: number }>) Writes homing offsets to the servo EEPROM. The offset is computed as `neutral - raw` by the platform. ### `context.saveCalibration(calibData: CalibrationData) Persists neutral calibration data to the backend. `CalibrationData` includes version, motor configurations with raw and URDF min/max values. ### `context.saveRangeCalibration(rangeData: Record) Persists range-of-motion calibration data to the backend. `rangeData` maps servo IDs to their calibration details, including raw and URDF min/max values. ``` -------------------------------- ### Robot Configuration API Source: https://context7.com/roboninecom/plugin-sdk/llms.txt Provides access to robot configuration, including joint-servo mapping and encoder-to-URDF value conversions. Available when a robot is connected and its model is known. ```APIDOC ## PluginRobotConfig — Joint-servo mapping and encoder helpers `context.robotConfig` (or `context.robot('default').robotConfig`) is available when a robot is connected and its model is known. It exposes bidirectional encoder ↔ URDF value converters and the servo neutral map. ### `context.robotConfig.modelId` (string) The model ID of the connected robot. ### `context.robotConfig.jointServoId` (Record) A mapping from joint names to their corresponding servo IDs. ### `context.robotConfig.servoNeutral` (Record) A map of servo IDs to their neutral encoder values. ### `context.robotConfig.encoderToJoint(servoId: number, encoderValue: number): number` Converts an encoder value to a URDF joint value (radians for revolute, meters for prismatic). ### `context.robotConfig.jointToEncoder(servoId: number, urdfValue: number): number` Converts a URDF joint value back to an encoder value. ### `context.robotConfig.neutralJointValue(servoId: number): number` Retrieves the neutral URDF value for a given servo ID. ``` -------------------------------- ### WorldView API Source: https://context7.com/roboninecom/plugin-sdk/llms.txt A ref-forwarded React component for 3D robot visualization. Use a `useRef` to call imperative methods. ```APIDOC ## WorldView API `context.WorldView` is a ref-forwarded React component. ### Props - **cameraDistanceScale** (number) - Scales the camera's distance. - **trackLivePosition** (boolean) - Whether to track the robot's live position. - **showTargetSphere** (boolean) - Whether to display the IK target sphere. - **motionMode** (string) - Sets the motion mode (e.g., 'realistic'). - **autoSolveIK** (boolean) - Whether to automatically solve IK. - **ghostJoints** (array of objects) - Defines ghost joints for visualization. - **onLoad** (function) - Callback when the robot model is loaded. - **onLoadProgress** (function) - Callback for loading progress. - **onTargetMove** (function) - Callback when the target sphere is moved. ### Methods (via ref) #### getJoints Returns an array of available joints with their properties. #### setJoint Sets the position of a specific joint. - **jointName** (string) - The name of the joint. - **position** (number) - The desired joint position (URDF rad / m). #### setLinkHighlight Highlights specific links with foreground and background colors. - **linkNames** (array of strings) - The names of the links to highlight. - **foregroundColor** (number) - The foreground color in hex. - **backgroundColor** (number) - The background color in hex. #### setTargetPosition Sets the position of the IK target sphere. - **position** ([number, number, number]) - The target position in metres. #### getJointWorldPosition Queries the world position of a joint. - **jointName** (string) - The name of the joint. Returns `Vector3 | null`. #### getJointWorldAxis Queries the world axis of a joint. - **jointName** (string) - The name of the joint. Returns `Vector3 | null`. #### resetJoints Resets all joints smoothly. - **smooth** (boolean) - Whether to reset smoothly. #### setRobotOpacity Sets the opacity of the robot model. - **opacity** (number) - The desired opacity (0.0 to 1.0). ``` -------------------------------- ### PluginContext API Source: https://context7.com/roboninecom/plugin-sdk/llms.txt The PluginContext provides access to various platform features and robot information. It's the primary interface for plugins to interact with the Robonine environment. ```APIDOC ## PluginContext API ### Description The `PluginContext` is passed to `PluginRoot` and serves as the sole API boundary between a plugin and the platform. It offers direct access to locale, user information, navigation, API fetching, toast notifications, camera data, and inter-service communication. ### Methods - **locale**: Access the current locale string. - **localize(translations: object): string**: Translates a given object of translations based on the current locale. - **user**: Access user information (PluginUser object) or null if not logged in. - **navigate(path: string): void**: Navigates the user interface to the specified path. - **apiFetch(path: string, options?: object): Promise** : Makes an authenticated API request to the specified path. - **toast**: An object with methods for displaying toast notifications. - **success(message: string): void** - **error(message: string): void** - **cameras**: An array of available camera objects, each with `id`, `label`, and `source`. - **service(serviceName: string): any**: Connects to and retrieves the API for a specified inter-plugin service. - **robot(role: string): RobotHandle**: Retrieves a `RobotHandle` for a given robot role (e.g., 'default', 'leader'). ``` -------------------------------- ### Servo ID Assignment API Source: https://context7.com/roboninecom/plugin-sdk/llms.txt API for assigning new servo IDs to servos via EEPROM. This operation requires the `robot.config` scope and must be performed with only one servo connected to the bus. ```APIDOC ## Servo config API — Write servo IDs to EEPROM `servo.setId` requires the `robot.config` scope. **Only one servo must be connected on the bus** when calling this method; connecting multiple servos with the same ID or calling this with multiple servos present will corrupt the bus. ### `context.servo.setId(newId: number) Assigns a new ID to the connected servo and writes it to its EEPROM. This is a destructive operation and requires user confirmation via `context.showSafetyWarning()`. ``` -------------------------------- ### Translation Handling in Plugin Source: https://github.com/roboninecom/plugin-sdk/blob/master/README.md Manage localized strings in a `translations.ts` file and dynamically select the appropriate language within your component using `useMemo` based on `context.locale`. Fallback to English if the locale is not found. ```typescript export const translations = { en: { title: 'My plugin', … }, ru: { title: 'Мой плагин', … }, } satisfies Record> ``` ```typescript const t = useMemo( () => translations[context.locale as keyof typeof translations] ?? translations.en, [context.locale], ) ``` -------------------------------- ### RobotHandle API Source: https://context7.com/roboninecom/plugin-sdk/llms.txt The RobotHandle provides methods for controlling a specific robot connection, including managing connection state, servo movements, and calibration. ```APIDOC ## RobotHandle API ### Description The `RobotHandle` represents a connection to a physical robot and is obtained via `context.robot(role)`. It provides access to connection details and methods for controlling servos and managing robot state. ### Properties - **connection**: An object containing connection status and robot details. - **connected**: boolean - **robotId**: string - **robotModel**: string - **virtual**: boolean - **remote**: boolean ### Methods - **openConnectDialog(): void**: Opens the robot connection dialog. - **showSafetyWarning(): Promise** : Displays a safety warning and returns a promise that resolves with the user's confirmation. - **servo**: An object containing servo control methods. - **setPosition(servoId: number, position: number, speed: number): Promise** : Sets a servo to a specific raw encoder position at a given speed. - **syncSetPositions(positions: Array<{ id: number, position: number }>): Promise** : Synchronously sets multiple servos to their specified positions in a single bus write. - **setJointPositions(positions: number[]): Promise** : Moves servos to target positions defined by URDF joint values. - **limitSpeed(speed: number): Promise** : Limits the maximum speed for all servos. `0` indicates maximum speed. - **registerEmergencyStop(callback: () => void): () => void** : Registers a callback function to be executed on emergency stop. Returns a cleanup function. - **readPosition(servoId: number): Promise** : Reads the raw encoder position of a servo. - **readRegisters(servoId: number, address: number, length: number): Promise** : Reads a specified number of registers from a servo starting at a given address. - **readJointPositions(): Promise** : Reads the current joint positions of the robot. ``` -------------------------------- ### Plugin Root Component with Localisation in React Source: https://context7.com/roboninecom/plugin-sdk/llms.txt A React component that uses the locale from the plugin context to select and display translated strings. Falls back to English if the locale is not found. ```typescript // src/plugin.tsx import type { PluginContext } from '@robonine/plugin-sdk' import { useMemo } from 'react' import { translations } from './translations' export function PluginRoot({ context }: { context: PluginContext }) { const t = useMemo( () => translations[context.locale as keyof typeof translations] ?? translations.en, [context.locale], ) return (

{t.title}

{t.status(context.connection.connected)}

) } ``` -------------------------------- ### Assign Servo ID via EEPROM Source: https://context7.com/roboninecom/plugin-sdk/llms.txt Use this to write a new servo ID to its EEPROM. Requires the 'robot.config' scope. Ensure only one servo is connected to the bus when calling this method. ```tsx import type { PluginContext } from '@robonine/plugin-sdk' export function PluginRoot({ context }: { context: PluginContext }) { async function assignServoId(newId: number) { // Warn the user — destructive operation const ok = await context.showSafetyWarning() if (!ok) return // One servo on bus — write new ID to its EEPROM via broadcast await context.servo.setId(newId) context.toast.success(`Servo ID set to ${newId}`) } return } ``` -------------------------------- ### Inspect Robot Configuration Source: https://context7.com/roboninecom/plugin-sdk/llms.txt Access robot configuration details like model ID, joint-servo mapping, and neutral encoder values. Also provides functions for converting between encoder and URDF values. ```tsx import type { PluginContext } from '@robonine/plugin-sdk' export function PluginRoot({ context }: { context: PluginContext }) { function inspectConfig() { const cfg = context.robotConfig if (!cfg) return console.log('Model:', cfg.modelId) // { shoulder: 1, elbow: 2, wrist: 3 } console.log('Joint→servo map:', cfg.jointServoId) // { 1: 2048, 2: 2048, 3: 2048 } console.log('Neutral encoders:', cfg.servoNeutral) // Convert encoder to URDF (rad for revolute, m for prismatic) const rad = cfg.encoderToJoint(1, 3000) // Convert URDF back to encoder const enc = cfg.jointToEncoder(1, rad) // Get neutral URDF value for servo 1 const neutral = cfg.neutralJointValue(1) console.log({ rad, enc, neutral }) } return } ``` -------------------------------- ### Declare Plugin Scopes for Different Plugin Types Source: https://context7.com/roboninecom/plugin-sdk/llms.txt Declare necessary `PluginScope` values in your `PluginManifest`. Undeclared scopes will be unavailable at runtime. The user must approve all requested scopes before the plugin can run. For UI-only plugins, an empty `scopes` array is sufficient. ```typescript import type { PluginManifest } from '@robonine/plugin-sdk' // UI-only plugin — no scopes needed const uiManifest: PluginManifest = { sdkVersion: '1', vendor: 'acme', slug: 'dashboard', name: { en: 'Dashboard' }, description: { en: 'Status dashboard.' }, icon: 'LayoutDashboard', scopes: [], } // Motion-control plugin const controlManifest: PluginManifest = { sdkVersion: '1', vendor: 'acme', slug: 'motion-control', name: { en: 'Motion Control' }, description: { en: 'Send position commands.' }, icon: 'Joystick', scopes: [ 'robot.control', 'robot.calibration', 'camera.read', 'install', 'user.auth', 'user.profile', 'user.read', 'robot.leader', 'robot.local', 'robot.config', ], } ``` -------------------------------- ### Calibrate Servo Homing Offsets and Range Source: https://context7.com/roboninecom/plugin-sdk/llms.txt Use this to calibrate servo homing offsets and range data. Requires the 'robot.calibration' scope. Ensure torque is disabled before positioning the arm. ```tsx import type { CalibrationData, JointCalibration, PluginContext } from '@robonine/plugin-sdk' export function PluginRoot({ context }: { context: PluginContext }) { async function runCalibration() { const confirmed = await context.showSafetyWarning() if (!confirmed) return // 1. Disable torque so the user can physically position the arm await context.servo.disableTorque() // 2. Write homing offsets: offset = neutral − raw is computed by the platform await context.servo.calibrateNeutralPositions([ { id: 1, neutral: 2048 }, { id: 2, neutral: 2048 }, { id: 3, neutral: 2048 }, ]) // 3. Persist neutral calibration to backend const calibData: CalibrationData = { version: 1, motors: { 1: { rawMin: 0, rawMax: 4095, urdfMin: -Math.PI, urdfMax: Math.PI }, 2: { rawMin: 512, rawMax: 3584, urdfMin: -1.57, urdfMax: 1.57 }, }, } await context.saveCalibration(calibData) // 4. Persist range-of-motion calibration const rangeData: Record = { 1: { rawMin: 100, rawMax: 3900, urdfMin: -3.0, urdfMax: 3.0 }, } await context.saveRangeCalibration(rangeData) context.toast.success('Calibration saved') } return } ``` -------------------------------- ### Define Plugin Translations in TypeScript Source: https://github.com/roboninecom/plugin-sdk/blob/master/README.md Define translations for different locales within your plugin. Ensure the structure matches `Record>` for proper locale handling. ```typescript export const translations = { en: { title: 'Hello robot', connectPrompt: 'Connect your robot to get started.', connectButton: 'Connect robot', greeting: 'Hello! Your robot is connected and ready.', }, ru: { title: 'Привет, робот', connectPrompt: 'Подключите робота, чтобы начать.', connectButton: 'Подключить робота', greeting: 'Привет! Ваш робот подключён и готов к работе.', }, } satisfies Record> ``` -------------------------------- ### Typed Translation Maps in TypeScript Source: https://context7.com/roboninecom/plugin-sdk/llms.txt Define localised strings using a typed map, ensuring type safety for translations. Supports dynamic string generation based on boolean or number arguments. ```typescript // src/translations.ts export const translations = { en: { title: 'Arm Telemetry', connectPrompt: 'Connect your robot to get started.', connectButton: 'Connect robot', status: (connected: boolean) => connected ? 'Connected' : 'Disconnected', jointCount: (n: number) => `${n} joint${n === 1 ? '' : 's'} available`, }, ru: { title: 'Телеметрия руки', connectPrompt: 'Подключите робота, чтобы начать.', connectButton: 'Подключить робота', status: (connected: boolean) => connected ? 'Подключён' : 'Отключён', jointCount: (n: number) => `Доступно суставов: ${n}`, }, } satisfies Record string)>> ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.