Skip to content

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.

bash
npm install @eniactech/flexdesigner-sdk

Quick Start

javascript
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

  1. Initialization: Plugin starts and connects to FlexDesigner
  2. Alive Event: Triggered when keys are loaded and ready
  3. Data Events: Triggered when users interact with keys
  4. 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:

  1. @napi-rs/canvas
  2. express
  3. axios

If you need integration of other modules, please let us know

Core Plugin APIs

plugin.start()

Starts the WebSocket connection and initializes the plugin.

javascript
plugin.start()

plugin.on(event, handler)

Registers an event handler for specific events.

Parameters:

  • event (string): Event type
  • handler (function): Event handler function
javascript
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.

javascript
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 number
  • key (object): Key object from events
  • type (string): 'draw' (default) or 'base64'
  • base64 (string): Base64 image data (when type is 'base64')
javascript
// 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 number
  • key (object): Key object
  • state (number): Target state index
  • message (string, optional): Display message
javascript
plugin.setMultiState(serialNumber, key, 2, 'State Changed')

plugin.setSlider(serialNumber, key, value)

Sets the value for slider keys.

Parameters:

  • serialNumber (string): Device serial number
  • key (object): Key object
  • value (number): Slider value
javascript
plugin.setSlider(serialNumber, key, 75)

Device Configuration & Control

plugin.sendControlCommand(serialNumber, command)

Sends control commands to the device

Parameters:

  • serialNumber (string): Device serial number
  • command (string): The command to send. One of the following:
    • "sys.sleep": Put the device to sleep
    • "sys.wake": Wake up the device
    • "haptic.click": Trigger a click vibration
javascript
plugin.sendControlCommand(serialNumber, 'sys.sleep') // Put the device to sleep
plugin.sendControlCommand(serialNumber, 'sys.wake') // Wake up the device
plugin.sendControlCommand(serialNumber, 'haptic.click') // Trigger a click vibration

plugin.setDeviceConfig(serialNumber, config)

Configures device settings.

Parameters:

  • serialNumber (string): Device serial number
  • config (object): Configuration object
javascript
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 number
  • msg (string): Message content (max 64 characters)
  • level (string): Message level ('info', 'warning', 'error', 'success')
  • icon (string, optional): Icon name
  • timeout (number, optional): Duration in ms (500-10000, default: 2000)
  • waitUser (boolean, optional): Wait for user interaction
javascript
plugin.showFlexbarSnackbarMessage(
    serialNumber, 
    'Hello from plugin!', 
    'info', 
    'bell', 
    3000
)

plugin.showSnackbarMessage(color, message, timeout?)

Shows a message in the FlexDesigner application.

javascript
plugin.showSnackbarMessage('success', 'Operation completed!', 3000)

System Integration

plugin.electronAPI(api, ...args)

Calls Electron APIs for system integration.

Supported APIs:

  • dialog.showOpenDialog
  • dialog.showSaveDialog
  • dialog.showMessageBox
  • dialog.showErrorBox
  • app.getAppPath
  • app.getPath
  • screen.getCursorScreenPoint
  • screen.getPrimaryDisplay
  • screen.getAllDisplays
javascript
// 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.

javascript
const content = await plugin.openFile('/path/to/file.txt')

plugin.saveFile(path, data)

Saves data to a file.

javascript
await plugin.saveFile('/path/to/output.txt', 'Hello World!')

Application Information

plugin.getAppInfo()

Gets FlexDesigner application information.

javascript
const appInfo = await plugin.getAppInfo()
// Returns: { version: "v1.0.0", platform: "win32" }

plugin.getOpenedWindows()

Gets list of opened windows on the system.

javascript
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.

javascript
const devices = await plugin.getDeviceStatus()
devices.forEach(device => {
    console.log(`Device: ${device.serialNumber}, Status: ${device.status}`)
})

Configuration Management

plugin.getConfig()

Retrieves plugin configuration.

javascript
const config = await plugin.getConfig()
console.log('Current config:', config)

plugin.setConfig(config)

Updates plugin configuration.

javascript
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
javascript
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
javascript
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.

javascript
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 key
  • backgroundType (string): 'base64' or 'draw'
  • backgroundData (string): Image data or key object
  • width (number): Key width in pixels (60-1000)
  • userData (object): Custom data associated with the key
javascript
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.

javascript
plugin.dynamickey.remove(serialNumber, key, 2)

plugin.dynamickey.move(serialNumber, key, srcIndex, dstIndex)

Moves a dynamic key from one position to another.

javascript
plugin.dynamickey.move(serialNumber, key, 0, 3)

plugin.dynamickey.setWidth(serialNumber, key, width)

Changes the width of the dynamic key container.

javascript
plugin.dynamickey.setWidth(serialNumber, key, 800)

plugin.dynamickey.draw(serialNumber, key, index, backgroundType, backgroundData, width)

Updates the visual appearance of a specific dynamic key.

javascript
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.

javascript
plugin.dynamickey.update(serialNumber, key, 0, { status: 'updated' })

plugin.dynamickey.refresh(serialNumber, key)

Refreshes the dynamic key display (recommended after width changes).

javascript
plugin.dynamickey.refresh(serialNumber, key)

Event Handling

Plugin Events

'plugin.alive'

Triggered when keys are loaded and ready for interaction.

javascript
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

javascript
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.

javascript
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.

javascript
plugin.on('plugin.config.updated', (payload) => {
    console.log('Configuration updated:', payload.config)
})

System Events

'system.shortcut'

Triggered when registered shortcuts are pressed.

javascript
plugin.on('system.shortcut', (payload) => {
    console.log(`Shortcut pressed: ${payload.shortcut}`)
})

'system.actwin'

Triggered when the active window changes.

javascript
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.

javascript
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.

javascript
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.

javascript
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.

javascript
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:

javascript
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 changes
  • Batch 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

  1. Plugin not starting: Ensure correct command-line arguments are provided
  2. Keys not updating: Check if the correct serial number is used
  3. Dynamic keys not displaying: Call refresh() after width changes
  4. Events not firing: Verify event handler registration

Debugging

Enable debug logging by setting the appropriate log level:

javascript
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.