Extension API¶
API reference for extension developers.
Constructor Arguments¶
Extensions receive four arguments:
constructor(deviceAPI, emit, state, html) {
this.device = deviceAPI // Device communication
this.emit = emit // Event emitter
this.state = state // Shared state
this.html = html // Template renderer
}
Device API¶
The deviceAPI object provides device communication:
execute(code)¶
Execute Python code on the device. Returns output as a string.
saveFile(path, content)¶
Write a file to the device filesystem.
readFile(path)¶
Read a file from the device. Returns content as string.
mkdir(path)¶
Create a directory. Succeeds silently if directory exists.
listDir(path)¶
List directory contents. Returns array of entries.
const files = await this.device.listDir('/lib')
// files = [{ name: 'sys', isDir: true }, { name: 'boot.py', isDir: false }]
removeFile(path)¶
Delete a file from the device.
State Object¶
The state object is shared across all extensions and the main app:
// Read connection status
if (this.state.isConnected) {
// Device is connected
}
// Store extension-specific data
if (!this.state.myExtension) {
this.state.myExtension = { config: {} }
}
Common State Properties¶
| Property | Type | Description |
|---|---|---|
isConnected |
boolean | Device connected via WebREPL |
deviceInfo |
object | Device metadata (board, flash, etc.) |
files |
array | Current file listing |
Emit Function¶
Trigger events in the application:
// Re-render the current panel
this.emit('render')
// Refresh file listing
this.emit('refresh-files')
HTML Template¶
The html function is a tagged template literal for rendering:
renderMyPanel() {
const items = ['A', 'B', 'C']
return this.html`
<div class="system-panel">
<div class="panel-header">
<h2>My Panel</h2>
</div>
<ul>
${items.map(item => this.html`<li>${item}</li>`)}
</ul>
</div>
`
}
Event Handlers¶
Use onclick, onchange, etc. with arrow functions:
return this.html`
<button onclick=${() => this.handleClick()}>
Click Me
</button>
<input
type="text"
value=${this.state.myExtension.name}
oninput=${(e) => this.handleInput(e.target.value)}
/>
`
Conditionals¶
return this.html`
${this.state.isConnected
? this.html`<p>Connected!</p>`
: this.html`<p>Not connected</p>`
}
`
Lifecycle Hooks¶
onInstall()¶
Called when user installs the extension while connected to a device:
async onInstall() {
// this.deviceFiles is injected by the loader
for (const [path, content] of Object.entries(this.deviceFiles)) {
await this.device.saveFile(path, content)
}
return true // Return false to indicate failure
}
getMenuItems()¶
Optional. Return dynamic menu items:
getMenuItems() {
const items = [{ id: 'main', label: 'Main' }]
// Add items based on state
if (this.state.devices?.length > 0) {
this.state.devices.forEach(d => {
items.push({ id: `device-${d.id}`, label: d.name })
})
}
return items
}
CSS Classes¶
Use these system classes for consistent styling:
| Class | Description |
|---|---|
.system-panel |
Standard panel container |
.panel-header |
Panel header with h2 and actions |
.panel-message |
Centered message text |
Define extension-specific styles in extension.json: