FlexDesigner SDK
A comprehensive SDK for developing plugins for FlexDesigner, providing seamless integration with Flexbar devices and rich interaction capabilities.
Installation
Prerequisites
Node.js version 20 or higher
FlexDesigner version 1.0.0 or higher.
Setup
Add FlexDesigner SDK to your project
In typical cases, you only need to create a plugin project using
flexcli, and the FlexDesigner SDK will be automatically installed.
npm install @eniactech/flexdesigner-sdk
Quick Start
const { plugin, logger, pluginPath, resourcesPath } = require("@eniactech/flexdesigner-sdk")
// Start the plugin
plugin.start()
// Handle plugin events
plugin.on('plugin.alive', (payload) => {
logger.info('Plugin loaded:', payload)
})
plugin.on('plugin.data', (payload) => {
logger.info('User interaction:', payload)
return 'Response from plugin backend!'
})
Core Concepts
Plugin Lifecycle
Initialization: Plugin starts and connects to FlexDesigner
Alive Event: Triggered when keys are loaded and ready
Data Events: Triggered when users interact with keys
Configuration: Plugin can read/write configuration data
Key Types
Standard Keys: Basic button functionality
Multi-State Keys: Cycle through different states
Slider Keys: Continuous value adjustment
Dynamic Keys: Dynamically managed key collections
Wheel Keys: Rotary encoder support
API Reference
Built-in Modules
To simplify the plugin development process, we have pre-integrated several modules that can be used directly:
If you need integration of other modules, please let us know
Core Plugin APIs
plugin.start()
Starts the WebSocket connection and initializes the plugin.
plugin.start()
plugin.on(event, handler)
Registers an event handler for specific events.
Parameters:
event(string): Event typehandler(function): Event handler function
plugin.on('plugin.data', (payload) => {
const { data, serialNumber } = payload
logger.info('Key pressed:', data.key.title)
return { status: 'success', message: 'Key handled!' }
})
plugin.off(event)
Unregisters an event handler.
plugin.off('plugin.data')
Drawing and Visual Updates
plugin.draw(serialNumber, key, type?, base64?)
Updates the visual appearance of a key.
Parameters:
serialNumber(string): Device serial numberkey(object): Key object from eventstype(string): ‘draw’ (default) or ‘base64’base64(string): Base64 image data (when type is ‘base64’)
// Update key based on key.style properties
plugin.draw(serialNumber, key, 'draw')
// Draw custom image
const base64Image = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...'
plugin.draw(serialNumber, key, 'base64', base64Image)
Key State Management
plugin.setMultiState(serialNumber, key, state, message?)
Sets the state for multi-state keys.
Parameters:
serialNumber(string): Device serial numberkey(object): Key objectstate(number): Target state indexmessage(string, optional): Display message
plugin.setMultiState(serialNumber, key, 2, 'State Changed')
plugin.setSlider(serialNumber, key, value)
Sets the value for slider keys.
Parameters:
serialNumber(string): Device serial numberkey(object): Key objectvalue(number): Slider value
plugin.setSlider(serialNumber, key, 75)
Device Configuration & Control
plugin.sendControlCommand(serialNumber, command)
Sends control commands to the device
Parameters:
serialNumber(string): Device serial numbercommand(string): The command to send. One of the following:“sys.sleep”: Put the device to sleep
“sys.wake”: Wake up the device
“hapic.click”: Trigger a click vibration
plugin.sendControlCommand(serialNumber, 'sys.sleep') // Put the device to sleep
plugin.sendControlCommand(serialNumber, 'sys.wake') // Wake up the device
plugin.sendControlCommand(serialNumber, 'hapic.click') // Trigger a click vibration
plugin.setDeviceConfig(serialNumber, config)
Configures device settings.
Parameters:
serialNumber(string): Device serial numberconfig(object): Configuration object
plugin.setDeviceConfig(serialNumber, {
sleepTime: 1000,
brightness: 100,
screenFlip: true,
vibrate: 'full',
autoSleep: true,
deviceName: 'My Flexbar',
cdcMode: true,
color: 'space black'
})
Messaging and Notifications
plugin.showFlexbarSnackbarMessage(serialNumber, msg, level, icon?, timeout?, waitUser?)
Displays a message on the Flexbar device.
Parameters:
serialNumber(string): Device serial numbermsg(string): Message content (max 64 characters)level(string): Message level (‘info’, ‘warning’, ‘error’, ‘success’)icon(string, optional): Icon nametimeout(number, optional): Duration in ms (500-10000, default: 2000)waitUser(boolean, optional): Wait for user interaction
plugin.showFlexbarSnackbarMessage(
serialNumber,
'Hello from plugin!',
'info',
'bell',
3000
)
plugin.showSnackbarMessage(color, message, timeout?)
Shows a message in the FlexDesigner application.
plugin.showSnackbarMessage('success', 'Operation completed!', 3000)
System Integration
plugin.electronAPI(api, …args)
Calls Electron APIs for system integration.
Supported APIs:
dialog.showOpenDialogdialog.showSaveDialogdialog.showMessageBoxdialog.showErrorBoxapp.getAppPathapp.getPathscreen.getCursorScreenPointscreen.getPrimaryDisplayscreen.getAllDisplays
// Show file dialog
const result = await plugin.electronAPI('dialog.showOpenDialog', {
properties: ['openFile'],
filters: [{ name: 'Images', extensions: ['png', 'jpg'] }]
})
// Get cursor position
const cursorPos = await plugin.electronAPI('screen.getCursorScreenPoint')
File Operations
plugin.openFile(path)
Reads a file from the filesystem.
const content = await plugin.openFile('/path/to/file.txt')
plugin.saveFile(path, data)
Saves data to a file.
await plugin.saveFile('/path/to/output.txt', 'Hello World!')
Application Information
plugin.getAppInfo()
Gets FlexDesigner application information.
const appInfo = await plugin.getAppInfo()
// Returns: { version: "v1.0.0", platform: "win32" }
plugin.getOpenedWindows()
Gets list of opened windows on the system.
const windows = await plugin.getOpenedWindows()
windows.forEach(win => {
console.log(`Window: ${win.title} (${win.bounds.width}x${win.bounds.height})`)
})
plugin.getDeviceStatus()
Gets status of connected devices.
const devices = await plugin.getDeviceStatus()
devices.forEach(device => {
console.log(`Device: ${device.serialNumber}, Status: ${device.status}`)
})
Configuration Management
plugin.getConfig()
Retrieves plugin configuration.
const config = await plugin.getConfig()
console.log('Current config:', config)
plugin.setConfig(config)
Updates plugin configuration.
await plugin.setConfig({ theme: 'dark', autoUpdate: true })
Performance Monitoring
plugin.sendChartData(chartDataArray)
Sends performance metrics for display in FlexDesigner.
Parameters:
chartDataArray(array): Array of chart data objects
plugin.sendChartData([
{
label: 'CPU Usage',
value: 45.2,
unit: '%',
baseUnit: '%',
baseVal: 45.2,
maxLen: 2,
category: 'system',
key: 'cpu'
},
{
label: 'Memory',
value: 2.1,
unit: 'GB',
baseUnit: 'MB',
baseVal: 2048,
maxLen: 3,
category: 'system',
key: 'memory'
}
])
Shortcuts Management
plugin.updateShortcuts(shortcuts)
Registers or unregisters keyboard shortcuts.
Parameters:
shortcuts(array): Array of shortcut objects
plugin.updateShortcuts([
{
shortcut: 'CommandOrControl+F1',
action: 'register'
},
{
shortcut: 'CommandOrControl+F2',
action: 'unregister'
}
])
Dynamic Key Management
The plugin.dynamickey object provides advanced key management capabilities.
plugin.dynamickey.clear(serialNumber, key)
Removes all dynamic keys from a container.
plugin.dynamickey.clear(serialNumber, key)
plugin.dynamickey.add(serialNumber, key, index, backgroundType, backgroundData, width, userData)
Adds a new dynamic key.
Parameters:
index(number): Position to insert the keybackgroundType(string): ‘base64’ or ‘draw’backgroundData(string): Image data or key objectwidth(number): Key width in pixels (60-1000)userData(object): Custom data associated with the key
plugin.dynamickey.add(
serialNumber,
key,
0,
'base64',
'data:image/png;base64,iVBORw0...',
200,
{ name: 'Dynamic Key 1', action: 'custom' }
)
plugin.dynamickey.remove(serialNumber, key, index)
Removes a dynamic key at the specified index.
plugin.dynamickey.remove(serialNumber, key, 2)
plugin.dynamickey.move(serialNumber, key, srcIndex, dstIndex)
Moves a dynamic key from one position to another.
plugin.dynamickey.move(serialNumber, key, 0, 3)
plugin.dynamickey.setWidth(serialNumber, key, width)
Changes the width of the dynamic key container.
plugin.dynamickey.setWidth(serialNumber, key, 800)
plugin.dynamickey.draw(serialNumber, key, index, backgroundType, backgroundData, width)
Updates the visual appearance of a specific dynamic key.
plugin.dynamickey.draw(
serialNumber,
key,
1,
'base64',
'data:image/png;base64,iVBORw0...',
200
)
plugin.dynamickey.update(serialNumber, key, index, userData)
Updates the user data for a dynamic key.
plugin.dynamickey.update(serialNumber, key, 0, { status: 'updated' })
plugin.dynamickey.refresh(serialNumber, key)
Refreshes the dynamic key display (recommended after width changes).
plugin.dynamickey.refresh(serialNumber, key)
Event Handling
Plugin Events
‘plugin.alive’
Triggered when keys are loaded and ready for interaction.
plugin.on('plugin.alive', (payload) => {
const { serialNumber, keys } = payload
keys.forEach(key => {
console.log(`Key loaded: ${key.cid} at position ${key.uid}`)
// Initialize key based on its type
if (key.cid === 'com.example.counter') {
key.style.showTitle = true
key.title = '0'
plugin.draw(serialNumber, key, 'draw')
}
})
})
‘plugin.dead’
Triggered when plugin keys are destroyed
plugin.on('plugin.dead', (payload) => {
const { serialNumber, keys } = payload
keys.forEach(key => {
console.log(`Key destoried: ${key.cid}`)
})
})
‘plugin.data’
Triggered when users interact with keys.
plugin.on('plugin.data', (payload) => {
const { serialNumber, data } = payload
const key = data.key
if (key.cid === 'com.example.button') {
console.log('Button pressed!')
return { status: 'success', message: 'Button handled' }
}
if (key.cid === 'com.example.slider') {
console.log(`Slider value: ${data.value}`)
}
})
‘plugin.config.updated’
Triggered when plugin configuration changes.
plugin.on('plugin.config.updated', (payload) => {
console.log('Configuration updated:', payload.config)
})
System Events
‘system.shortcut’
Triggered when registered shortcuts are pressed.
plugin.on('system.shortcut', (payload) => {
console.log(`Shortcut pressed: ${payload.shortcut}`)
})
‘system.actwin’
Triggered when the active window changes.
plugin.on('system.actwin', (payload) => {
const { oldWin, newWin } = payload
console.log(`Window changed: ${oldWin.title} -> ${newWin.title}`)
})
Device Events
‘device.status’
Triggered when device connection status changes.
plugin.on('device.status', (devices) => {
devices.forEach(device => {
if (device.status === 'connected') {
console.log(`Device connected: ${device.serialNumber}`)
// Configure the newly connected device
plugin.setDeviceConfig(device.serialNumber, {
brightness: 80,
deviceName: 'My Plugin Device'
})
}
})
})
UI Events
‘ui.message’
Triggered when receiving messages from the plugin’s UI.
plugin.on('ui.message', async (payload) => {
console.log('Message from UI:', payload)
if (payload.action === 'test') {
// Perform test operations
await testAPIs()
return 'Test completed!'
}
return 'Message received!'
})
‘ui.log’
Handles log messages from the plugin’s UI (automatically handled).
Utilities and Helpers
Logger
The SDK provides a logger instance for debugging and monitoring.
const { logger } = require("@eniactech/flexdesigner-sdk")
logger.info('Information message')
logger.warn('Warning message')
logger.error('Error message')
logger.debug('Debug message')
Path Utilities
Access to plugin-specific paths.
const { pluginPath, resourcesPath } = require("@eniactech/flexdesigner-sdk")
console.log('Plugin directory:', pluginPath)
console.log('Resources directory:', resourcesPath)
Complete Example
Here’s a comprehensive example demonstrating various SDK features:
const { plugin, logger, pluginPath } = require("@eniactech/flexdesigner-sdk")
const { createCanvas } = require('@napi-rs/canvas')
// Store key data
const keyData = {}
// Plugin lifecycle
plugin.start()
// Handle key loading
plugin.on('plugin.alive', (payload) => {
const { serialNumber, keys } = payload
keys.forEach(key => {
keyData[key.uid] = key
switch (key.cid) {
case 'com.example.counter':
// Initialize counter
keyData[key.uid].counter = 0
key.style.showTitle = true
key.title = 'Click Me!'
plugin.draw(serialNumber, key, 'draw')
break
case 'com.example.slider':
// Set initial slider value
plugin.setSlider(serialNumber, key, 50)
break
case 'com.example.dynamic':
// Setup dynamic keys
setupDynamicKeys(serialNumber, key)
break
}
})
})
// Handle user interactions
plugin.on('plugin.data', (payload) => {
const { serialNumber, data } = payload
const key = data.key
switch (key.cid) {
case 'com.example.counter':
// Increment counter
keyData[key.uid].counter++
key.title = `${keyData[key.uid].counter}`
plugin.draw(serialNumber, key, 'draw')
break
case 'com.example.wheel':
// Handle wheel rotation
showWheelFeedback(serialNumber, data.delta)
break
}
})
// Handle device connections
plugin.on('device.status', (devices) => {
devices.forEach(device => {
if (device.status === 'connected') {
logger.info(`Device connected: ${device.serialNumber}`)
// Configure device
plugin.setDeviceConfig(device.serialNumber, {
brightness: 100,
deviceName: 'SDK Example Device',
autoSleep: true
})
}
})
})
// Helper function for dynamic keys
function setupDynamicKeys(serialNumber, key) {
plugin.dynamickey.clear(serialNumber, key)
// Add multiple dynamic keys
for (let i = 0; i < 3; i++) {
const canvas = createCanvas(200, 60)
const ctx = canvas.getContext('2d')
// Draw custom background
ctx.fillStyle = `hsl(${i * 120}, 70%, 50%)`
ctx.fillRect(0, 0, 200, 60)
ctx.fillStyle = 'white'
ctx.font = '20px Arial'
ctx.textAlign = 'center'
ctx.fillText(`Key ${i}`, 100, 35)
const imageData = canvas.toDataURL()
plugin.dynamickey.add(
serialNumber,
key,
i,
'base64',
imageData,
200,
{ id: i, name: `Dynamic Key ${i}` }
)
}
}
// Performance monitoring
setInterval(() => {
const memUsage = process.memoryUsage()
plugin.sendChartData([
{
label: 'Memory Usage',
value: memUsage.heapUsed / 1024 / 1024,
unit: 'MB',
baseUnit: 'bytes',
baseVal: memUsage.heapUsed,
maxLen: 3,
category: 'system',
key: 'memory'
}
])
}, 5000)
// Register shortcuts
setTimeout(() => {
plugin.updateShortcuts([
{
shortcut: 'CommandOrControl+Shift+F1',
action: 'register'
}
])
}, 1000)
logger.info('Plugin example started successfully!')
Best Practices
Performance
Use
plugin.dynamickey.refresh()after width changesBatch multiple dynamic key operations when possible
Optimize image sizes for better performance
Error Handling
Always wrap async operations in try-catch blocks
Validate key types before calling type-specific methods
Handle device disconnection gracefully
User Experience
Provide visual feedback for user interactions
Use appropriate message levels for notifications
Keep snackbar messages concise and informative
Development
Use the logger for debugging instead of console.log
Test with multiple device connections
Validate configuration before applying changes
Troubleshooting
Common Issues
Plugin not starting: Ensure correct command-line arguments are provided
Keys not updating: Check if the correct serial number is used
Dynamic keys not displaying: Call
refresh()after width changesEvents not firing: Verify event handler registration
Debugging
Enable debug logging by setting the appropriate log level:
logger.debug('Debug information')
Monitor WebSocket connections and ensure the plugin can communicate with FlexDesigner.
License
This SDK is provided under the terms specified by EniacTech. Please refer to your license agreement for usage terms and conditions.