Skip to content
Luxtron-SystemsPublic

About

A simple DALI simulator in Python for agentic application testing.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Latest commit

 

History

8 Commits

Folders and files

Repository files navigation

dali-sim

An offline DALI lighting control simulator for testing AI agents before you point them at a real site.

You get one or more simulated DALI lines full of control gear and sensors. An agent drives them through MCP, REST or plain Python, and a dashboard shows what is actually happening. It works at command level: no bit timing, but the IEC 62386 behaviour is all there. That covers short/group/broadcast addressing, DAPC and fades, the 16 scenes and 16 groups, min/max/power-on/system-failure levels, the status byte and queries, DT8 colour (Tc, RGBWAF, xy), DT6 dimming curves, random-address commissioning, and part 103 sensors and push buttons. The trace still shows real forward frames (FF 05 BC RECALL_MAX_LEVEL), so it reads like a bus monitor.

Run it

uv sync
uv run dali-sim serve office

Then open http://127.0.0.1:8765 for the dashboard. REST lives under /api (OpenAPI docs at /docs) and MCP over streamable HTTP is at /mcp.

For an MCP client that wants stdio (Claude Desktop, Claude Code, etc.):

{
  "mcpServers": {
    "dali": {
      "command": "uv",
      "args": ["--directory", "/path/to/dali-sim", "run", "dali-sim", "mcp", "office", "--agent-mode"]
    }
  }
}

The stdio server still runs the dashboard on :8765 so you can watch the agent work (--no-dashboard turns it off).

Useful flags on serve and mcp:

flag what it does
--virtual-clock time only moves when you call sim_advance_time / POST /api/sim/advance, which makes runs repeatable
--agent-mode hides the sim_* MCP tools so the agent under test can't peek at ground truth or inject its own faults
--seed N RNG seed for random addresses and fault rolls
--trace prints bus traffic to stderr

Other commands: dali-sim state (table of a running sim), dali-sim trace -f, dali-sim events -f, dali-sim validate my-site.yaml, dali-sim sites.

Sites

Three presets ship with the package (src/dali_sim/sites/):

  • office: one line with tunable white open plan, an RGBW meeting room cove, a corridor on a PIR, a wall switch and a daylight sensor. The built-in controller handles the PIRs and the switch.
  • retail: two lines (sales floor and back of house) with scenes for opening, trading, closing, cleaning and night. There are no controller rules, so the agent runs the shop.
  • warehouse: 18 high bays fresh out of the box with no short addresses. The agent has to commission, group and scene them.

Write your own by copying one. The format looks like this:

name: my-site
seed: 1
buses:
  - id: line1
    groups: {0: lobby}
    scenes: {0: day, 15: night}
    gear:
      - name: lobby_dl_{n}      # count expands {n} and bumps the address
        count: 4
        address: 0              # leave out for unaddressed gear
        type: tc                # led | tc | rgbwaf | xy | colour
        room: Lobby
        groups: [lobby]
        fade_time: 4
        scenes: {day: 254, night: {level: 80, kelvin: 2700}}
    inputs:
      - name: lobby_pir
        address: 0
        instances:
          - {type: occupancy, hold_time: 300}
          - {type: light, daylight: 400, sees: {lobby_dl_1: 120}}
controller:
  enabled: true
  rules:
    - when: {input: lobby_pir, event: occupied}
      do: {target: lobby, scene: day}

Quote "on" and "off" in YAML, otherwise they turn into booleans.

Targets

Every control call takes a target, which can be:

  • a gear name
  • a group name
  • room:<name>
  • all
  • a raw address: A0-A63, G0-G15, BC or BCU

On multi-bus sites, prefix the target with the bus, e.g. line2:G0.

What the agent sees

Gateway tools behave like a real gateway: set_level, recall_scene, set_colour, store_scene, set_group, configure, query, read_status, send_command, send_raw_frame, scan_bus, commission_bus, read_sensor, read_events, read_trace and friends. Commands are fire-and-forget like on a real bus, so if a frame is dropped the agent only finds out by querying.

Sim tools (sim_*, also exposed under /api/sim/*) are the world side:

  • people walking past sensors and pressing buttons
  • daylight
  • the clock
  • resetting the site
  • faults:
    • lamp_failure, gear_failure, no_response, input_no_response
    • bus_power_down, mains_power_cycle
    • drop_rate, garble_rate, latency_ms

A typical test harness drives the scenario over REST and gives the agent MCP in --agent-mode.

Python

from dali_sim import load_site

site = load_site("office", virtual_clock=True)
site.set_level("open_office", percent=60)
site.advance(3)
print(site.read_status("office_dl_1"))

Tests

uv run pytest

Not modelled

These are left out:

  • bit-level timing and the 24-bit part 103 frames (input devices are event-level)
  • memory banks beyond bank 0
  • writable memory
  • DT8 primary-N channels
  • the multi-master arbitration

About

A simple DALI simulator in Python for agentic application testing.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages