Skip to content
View as Markdown

Lunatone DALI-2 USB (LunatoneLuba) ​

DALI-2 bus master through a Lunatone DALI-2 USB interface. One connector controls every piece of DALI control gear on the interface's bus: set levels, read status and LED failure flags, read and write the DALI configuration, read D4i identity / energy / diagnostics banks, and give gear short addresses.

The driver talks Lunatone's LUBA protocol over the interface's USB virtual COM port (38400 baud, 8N1). Tier-1 free JavaScript driver, distributed as a .muxdriver package. Needs Muxit 0.43.1 or later: LUBA frames contain bytes ≥ 0x80, which older builds could not send from a JS driver. On an older build the writeMode property reads "text" and those frames fail with a clear error instead of going out corrupted.

Connector config ​

The driver's template is a connector named dali that pairs with the DALI status dashboard and the examples/dali-monitor script in the example workspace:

javascript
// connectors/dali.js
export default {
  driver: "LunatoneLuba",
  config: {
    port: "COM5",        // Required. The interface's COM port ("/dev/ttyACM0" on Linux).
    line: 0,             // Optional. DALI line of the interface. Default 0.
    busPower: true,      // Optional. Switch the interface's own bus supply on (true) or off (false).
                         //           Omit to leave it as it is.
    timeoutMs: 500,      // Optional. Timeout per LUBA request. Default 500.
    priority: 1,         // Optional. DALI frame priority 1-5. Default 1.
    autoOpen: true,      // Optional. Open the port when the connector starts. Default true.
  },
};

The full template adds, on top of the driver's own properties and actions:

  • Dashboard properties, computed from the cached busStatus result, so polling them costs no bus traffic: busState (OK / FAIL / NO BUS / NO UNITS / NO DATA / OFFLINE), statusText, failuresText, unitCount, busPowered, levels (array, one level per unit) and lastUpdate. broadcastLevel is writable: writing it sets every unit.
  • Short methods: refresh and rescan (update the status, return the text summary), overview(rescan?) and summary(rescan?), allOff(), level(address, level), actual(address), status(address), settings(address), configure(address, settings), d4i(address), bank(address, bank, first?, last?), linear(address), identify(address), and handOver() / takeBack() to release and re-open the port.

Create the connector from the Extensions panel to get it, or copy connectors/_examples/dali.js.

A DALI bus needs a supply. If no gear on the bus provides one, set busPower: true so the interface supplies it; busStatus reports NO VOLTAGE otherwise.

Addresses ​

Actions that send a command take address as a short address 0–63, a group "g0"–"g15", or "broadcast". Queries and reads need a short address. Levels are DALI arc power values 0–254 (0 = off); the gear clamps them to its MIN/MAX level.

Properties ​

PropertyTypeDescription
connectedboolCOM port open and LUBA interface configured
portstringSerial port of the interface
unitsobjectResult of the last busStatus call (cached, no bus traffic); null before the first call
interfaceSettingsobject{mode, eventFilter, hardware, busPowerOn} as applied at open
writeModestring"raw" (binary-safe), "hex" (test transport) or "text" (Muxit older than 0.43.1)
lastErrorstringLast bus or protocol error with ISO timestamp; empty when none

All properties are read-only. Polling them costs no bus traffic.

Actions ​

Overview and port

ActionArgsReturns
busStatusrescan?{ok, time, interface, units, text}: per-unit level and failures plus a readable summary. Never throws for bus problems. First call (or rescan: true) scans addresses 0–63 (~2 s).
open—"OK". Open the port and configure the interface.
release—"OK". Close the port so another program (e.g. MultiOne) can use the interface.
interfaceInfo—{gtin, id, pcbVersion, assemblyVersion, articleNumber, info, raw}
lineStatus—{line, available, nextId, framesInBuffer, busVoltageError, initialiseMode, quiescentMode}
scan—[{address, duplicate}]: short addresses that answer QUERY CONTROL GEAR PRESENT

Light level

ActionArgsReturns
setLeveladdress, level{frame, result, answer}. Direct Arc Power 0–254.
off / recallMax / recallMinaddress{frame, result, answer}
identifyaddress{frame, result, answer}. The unit signals itself for ~10 s (DALI-2).

Status and settings (short address only)

ActionArgsReturns
queryActualLeveladdress0–254, or null when no gear answers
queryStatusaddressQUERY STATUS decoded into flags
readStatusaddressStatus flags, actual level, gear failure, DT6 LED failure flags, failures (list of strings) and ok
readSettingsaddressDALI version, device types, light source type, MIN/MAX/power-on/system-failure levels, fade, groups; dimming curve for DT6 gear
queryaddress, opcodeThe 8-bit answer to any DALI query opcode, or null
queryDimmingCurveaddress"logarithmic", "linear", or null
readMemoryBankaddress, bank, first?, last?{bank, implemented, bytes, hex, decoded?}
readD4iaddressBanks 0, 1, 202, 205 and 206 decoded: identity, luminaire data, energy, gear and light-source diagnostics (~200 transactions)

Changing stored configuration

ActionArgsReturns
storeSettingsaddress, settingsWrites maxLevel, minLevel, powerOnLevel, systemFailureLevel, fadeTime, fadeRate, groups; for a short address, reads back and lists mismatches
setDimmingCurveaddress, curveDT6: "linear" or "logarithmic"
resetaddressDALI RESET: levels, fade, groups and scenes back to reset values (short address kept)
setShortAddressaddress, newAddressChange or delete (null) one unit's short address; refuses an address in use
assignAddressesmode?, first?[{address, random}]. "unaddressed" (default) numbers gear without a short address; "all" deletes every short address first. ~3–4 s per unit.
sendRaw16hex, answer?, twice?Send any 16-bit forward frame, e.g. "FFA0"

These change what the gear stores, and assignAddresses with mode: "all" renumbers the whole bus. The template's ai.instructions tell the AI to ask before calling them. The driver documents each action's full return format in its details, which the AI reads on demand.

Streams ​

StreamContent
busEventsBus-level events as JSON strings: busError, systemFailure, busRestored, busSupplyWarning

Bind a terminal widget to dali:busEvents to watch them.

Example script ​

javascript
const dali = connector("dali");

const st = dali.busStatus({ rescan: true });
log.info(st.text);

dali.setLevel("broadcast", 128);
const d4i = dali.readD4i(0);
log.info(`unit 0: ${d4i.energy?.activeEnergyWh} Wh, ${d4i.gearDiagnostics?.temperatureC} °C`);

Sharing the interface with MultiOne ​

Settings that are not part of DALI (such as a Philips driver's output current) need the vendor's tool. Call release to free the COM port, use the other program, then open to take the interface back.

Notes ​

  • All bus access is serialised: one transaction, or one multi-frame sequence such as DTR0 + STORE or a memory-bank read, at a time.
  • D4i decoding follows IEC 62386-102 ed2 (bank 0) and DiiA Parts 251–253. The raw bytes are always returned next to the decoded fields; check them against your gear before relying on a decoded value in limits.
  • Revision history: drivers/js/lunatone-luba/CHANGELOG.md.

Muxit — Hardware Orchestration Platform