Skip to content

Driver SDK Reference

Build device drivers for Muxit as standalone .NET class libraries (Tier 3) or as JavaScript modules (Tier 1). C# drivers implement IConnectorDriver from the shared SDK. JS drivers export a default object with meta, init, get, set, execute, and shutdown methods.

Driver Tiers

TierTypeLocationStatus
0Built-in (C#)Compiled into MuxitServerAlways available
1JavaScriptworkspace/drivers/*.driver.jsActive (sandboxed V8)
2SCPIworkspace/drivers/*.scpiDisabled
3DLL Extension (C#)workspace/drivers/free/ or premium/Active

JS Drivers (Tier 1)

Structure

javascript
export default {
  meta: {
    name: "MyDriver",
    version: "1.0.0",
    description: "Short description of what this driver does.",
    group: "instruments",  // DriverGroup: "instruments", "motion", "communication", "utilities"
    properties: { voltage: { type: "number", access: "rw", unit: "V", description: "Output voltage" } },
    actions: { reset: { description: "Reset device" } },
    streams: ["output"],
  },
  async init(config) { /* set up transport, initialize device */ },
  async get(property) { /* return property value */ },
  async set(property, value) { /* set property value */ },
  async execute(action, args) { /* execute action, return result */ },
  async shutdown() { /* clean up */ },
};

Available APIs

APIDescription
createTcpTransport(host, port, opts)Create TCP transport ({ delimiter, timeout })
createSerialTransport(path, opts)Create serial transport ({ baudRate, delimiter, timeout })
__emitStream(stream, data)Emit streaming data
log.debug/info/warn/error(...)Logging
timestamp()ISO timestamp string

Security Sandbox

JS drivers run in an isolated ClearScript V8 engine with these restrictions:

  • require(), import() — blocked
  • process, global — deleted from globalThis
  • No access to fs, net, child_process, or any Node.js APIs
  • Path traversal — .driver.js files must reside within workspace/drivers/
  • Name collision — JS drivers cannot shadow built-in (Tier 0) driver names

Built-in Drivers (Tier 0)

DriverNameDescription
WebcamDriverWebcamUSB webcam capture via OpenCV
OnvifDriverOnvifONVIF IP camera discovery, streaming, PTZ
TestDriverTestDeviceTest/example driver exercising all features
FileAccessDriverFileAccessSandboxed file I/O restricted to a base directory
MqttBridgeDriverMqttBridgeMQTT broker bridge (ESP32, Arduino, Home Assistant, Node-RED)
VisionDriverVisionComputer vision (OpenCV detection, color/contour tracking) — license-gated

FileAccess Driver

Provides read/write/list/delete operations sandboxed to a single directory (typically workspace/data/).

Config:

KeyTypeDefaultDescription
basePathstring(required)Root directory for file operations
maxFileSizeMBint10Max file size for read/write in MB
createIfMissingbooltrueCreate base directory if missing

Actions:

ActionArgsReturnsDescription
listFilespath?string[]List files (relative paths)
readTextpathstringRead text file
writeTextpath, content"OK"Write text file
appendTextpath, content"OK"Append to text file
deleteFilepath"OK"Delete a file
renamefrom, to"OK"Rename/move within sandbox
fileInfopathobjectFile metadata

C# Drivers (Tier 3)

Quick Start

  1. Copy drivers/Muxit.Driver.Template/ to drivers/Muxit.Driver.YourDevice/
  2. Rename in .csproj: <AssemblyName>YourDevice</AssemblyName>
  3. Implement your driver logic
  4. Build: dotnet build drivers/Muxit.Driver.YourDevice -c Release
  5. Copy YourDevice.dll to workspace/drivers/free/
  6. Test: node start.js cli, then scan and init YourDevice

IConnectorDriver Interface

csharp
public interface IConnectorDriver
{
    string Name { get; }
    string? Version => null;
    string? Description => null;   // Human-readable driver description

    Task InitAsync(Dictionary<string, object?>? config);
    Task ShutdownAsync();

    IEnumerable<PropertyDescriptor> GetProperties();
    IEnumerable<ActionDescriptor> GetActions();

    Task<object?> GetAsync(string property);
    Task SetAsync(string property, object? value);
    Task<object?> ExecuteAsync(string action, object? args);

    bool SupportsStreaming => false;
    IEnumerable<string> GetStreams() => [];
    Action<string, string>? StreamEmitter { get; set; }
}

DriverGroup Enum

Every driver should declare a group for UI categorization using the [DriverGroup] attribute:

csharp
using Muxit.Driver.Sdk;

[DriverGroup(DriverGroup.Instruments)]
public sealed class MyDriver : IConnectorDriver { ... }
ValueDescription
DriverGroup.InstrumentsMeasurement devices (oscilloscopes, multimeters, spectrometers)
DriverGroup.MotionRobots, CNC, stages, actuators
DriverGroup.CommunicationSerial monitors, MQTT bridges, protocol adapters
DriverGroup.UtilitiesTest devices, file access, vision, general-purpose

For JS drivers, set meta.group to the lowercase enum name (e.g., "instruments", "motion").

Lifecycle

  1. Constructor — must be parameterless and public. Do NOT open connections here.
  2. InitAsync(config) — open connections, start background tasks
  3. GetAsync / SetAsync / ExecuteAsync — normal operation
  4. ShutdownAsync — release all resources

Property Types

Type stringC# type (Get)C# type (Set)
"string"stringstring
"int"intint
"double"doubledouble
"bool"boolbool
"double[]"double[]object?[]
"object"Dictionary<string, object?>Dictionary<string, object?>

Automatic type conversion: All driver return values are automatically converted to native JavaScript types when accessed from connector configs or scripts. C# double[] becomes a JS array you can .map(), .filter(), or spread (...). C# Dictionary<string, object?> becomes a plain JS object. No manual conversion is needed in connector or script code.

Access Modes

AccessMeaning
"R"Read-only
"R/W"Read-write
"W"Write-only

Config Helpers

csharp
using static Muxit.Driver.Sdk.DriverConfig;

public Task InitAsync(Dictionary<string, object?>? config)
{
    var host = GetString(config, "ip", "192.168.1.100");
    var port = GetInt(config, "port", 5000);
    var timeout = GetDouble(config, "timeout", 30.0);
    var verbose = GetBool(config, "verbose", false);
}

Actions & Arguments

Actions are declared with ActionDescriptor. Each argument is described with ArgDescriptor(name, type, description):

csharp
public IEnumerable<ActionDescriptor> GetActions() => new[]
{
    new ActionDescriptor("reset", "Reset to factory defaults"),
    new ActionDescriptor("moveTo", "Move to position", [
        new ArgDescriptor("x", "double", "X coordinate in mm"),
        new ArgDescriptor("y", "double", "Y coordinate in mm"),
        new ArgDescriptor("z", "double", "Z coordinate in mm"),
    ]),
};

Descriptors:

csharp
// Property: name, type, access, unit, description
public record PropertyDescriptor(string Name, string Type, string Access,
    string Unit = "", string Description = "");

// Action argument: name, type, description
public record ArgDescriptor(string Name, string Type, string Description = "");

// Action: name, description, args
public record ActionDescriptor(string Name, string Description = "",
    List<ArgDescriptor>? Args = null);

Streaming

csharp
public bool SupportsStreaming => true;
public IEnumerable<string> GetStreams() => new[] { "data", "video" };
public Action<string, string>? StreamEmitter { get; set; }

// Emit from background thread:
StreamEmitter?.Invoke("data", jsonString);

Minimal Driver Example

csharp
using Muxit.Driver.Sdk;
using static Muxit.Driver.Sdk.DriverConfig;

namespace Muxit.Driver.YourDevice;

public sealed class YourDeviceDriver : IConnectorDriver
{
    public string Name => "YourDevice";
    public string? Version => "1.0.0";
    public string? Description => "Your device description for documentation and AI.";
    public Action<string, string>? StreamEmitter { get; set; }

    public Task InitAsync(Dictionary<string, object?>? config)
    {
        var host = GetString(config, "host", "localhost");
        return Task.CompletedTask;
    }

    public Task ShutdownAsync() => Task.CompletedTask;

    public IEnumerable<PropertyDescriptor> GetProperties() => new[]
    {
        new PropertyDescriptor("value", "double", "R", "", "Current reading"),
    };

    public IEnumerable<ActionDescriptor> GetActions() => Array.Empty<ActionDescriptor>();

    public Task<object?> GetAsync(string property) => property switch
    {
        "value" => Task.FromResult<object?>(42.0),
        _ => throw new ArgumentException($"Unknown property: {property}"),
    };

    public Task SetAsync(string property, object? value) =>
        throw new ArgumentException($"Property '{property}' is read-only");

    public Task<object?> ExecuteAsync(string action, object? args) =>
        throw new ArgumentException($"Unknown action: {action}");
}

Building & Testing

bash
# Build all drivers
node build.js drivers

# Build specific driver
dotnet build drivers/Muxit.Driver.YourDevice -c Release

# Test with CLI
node start.js cli
# Then: scan, init, get, set, exec, meta, shutdown

Project Structure

drivers/
├── Muxit.Driver.Sdk/           # Shared SDK
├── Muxit.Driver.Template/      # Reference implementation
├── Muxit.Driver.Fairino/       # Robot driver
├── Muxit.Driver.Avantes/       # Spectrometer driver
├── Muxit.Driver.Scpi/          # Generic SCPI instrument driver
└── Muxit.Driver.YourDevice/
    ├── Muxit.Driver.YourDevice.csproj
    └── YourDeviceDriver.cs

Free vs Premium Drivers

TypeSigned?License?Who builds?
Official (free)YesNoMuxit team
Official (premium)YesYesMuxit team
Third-party (free)NoNeverAnyone

Free drivers are always free. Build the DLL, drop it in workspace/drivers/free/, done.

Muxit — Hardware Orchestration Platform