TapHome
Official · TapHome Lighting

Resi DALI

Runtime DALI lighting control for the RESI-DALI-SIO and RESI-DALI-ETH Modbus-DALI gateway. Per-gear dimming for up to 64 control gears, 16 broadcast groups, and per-gear DALI 1.0 status, lamp/power-failure and min/max-level diagnostics over Modbus RTU.

ResiModbus RTU80 devicesRevision 1.3 Last updated: November 15, 2026
Resi DALI

Import into the TapHome appHardware › Modbus › Add from template

Download Template
On this page

Requires external gateway

Resi RESI-DALI-PS — product page

Integration guide

The Resi DALI template is the runtime operational counterpart to the Resi Dali - Configuration commissioning tool. It connects TapHome to a fully addressed DALI lighting bus through the RESI-DALI-SIO or RESI-DALI-ETH Modbus-DALI gateway and exposes day-to-day control for up to 64 individually addressed DALI control gears (short addresses 0–63) and 16 DALI broadcast groups (0–15), plus per-gear DALI status, control-gear-failure and lamp-failure diagnostics.

Communication with the gateway uses Modbus RTU at 9600 baud 8N1. Brightness writes go to H:510 LAMP LEVEL (per-gear) and H:520 GROUP LEVEL (per-group); per-gear configuration commands and queries are tunnelled through H:511 LAMP COMMAND / H:512 LAMP COMMAND + REPEAT, with the gateway’s H:550 DALI STATUS register polled for response readiness.

This template assumes DALI short addresses are already programmed on the bus. If gears are still un-addressed, use the sibling Resi Dali - Configuration template (or the manufacturer’s free MODBUSConfigurator / DCA tool) to run the DALI 2.0 random-addressing wizard first — see Relationship with the commissioning template below.

Hardware connection

The RESI-DALI-SIO (RS-485) and RESI-DALI-ETH (Ethernet) are DIN-rail master gateways that sit between a serial/Ethernet host and a DALI lighting bus. The SIO ships in a CEM17 housing (17.5 × 90 × 56.4 mm) and the ETH in a wider CEM35 housing (35.8 × 90 × 56.4 mm) to fit the RJ45 connector. Neither gateway powers the DALI bus by itself — a companion DALI bus power supply (RESI-DALI-PS or any compliant 200 mA DALI PSU) is mandatory.

Connecting the RESI-DALI-SIO (RS-485)

RESI-DALI-SIO connection diagram — external DALI PSU (top) and combined with RESI-DALI-PS (bottom)

Terminals on the left side of the RESI-DALI-SIO (top → bottom): GND, +12..48V=, RS485 DATA+ (A+), RS485 DATA- (B-), RS485 GND, RS232 TX, RS232 RX, RS232 GND. The DALI bus terminals on the opposite side are D+ / N/C / D- (3-pin USLIM block, the middle N/C pin stays unconnected).

For a TapHome installation use the RS-485 interface:

  • RS485 DATA+ (A+) → A+/D+ on the TapHome RS-485 bus
  • RS485 DATA- (B-) → B-/D- on the TapHome RS-485 bus
  • RS485 GND → GND on the TapHome power supply (recommended for stable communication)
  • +12..48V= / GND → 12–48 V DC primary power

Connecting the RESI-DALI-ETH (Ethernet)

RESI-DALI-ETH connection diagram — external DALI PSU (top) and combined with RESI-DALI-PS (bottom)

The Ethernet variant has the same DALI bus terminals (D+ / N/C / D-) and primary power inputs (GND, +12..48V=), plus a single RJ45 connector for 10/100 Mbit Ethernet. Factory-default IP settings are 192.168.0.191 / 255.255.255.0 / 192.168.0.1 with Modbus Unit ID 255; the embedded web UI uses credentials RESI / RESI.

Because this is a Modbus RTU template, TapHome does not talk to the RESI-DALI-ETH via native Modbus/TCP. Use it instead in transparent RTU-over-Ethernet mode, or place the SIO behind a serial-to-IP bridge (Moxa NPort, USR-TCP232) — see Configuration → RTU-over-TCP setup below.

DALI bus power supply (RESI-DALI-PS)

RESI-DALI-PS — powering the DALI bus standalone (top) and combined with a SIO/ETH gateway (bottom)

The RESI-DALI-PS delivers up to 200 mA on the DALI bus (short-circuit current ~225 mA, bus voltage typically 14–18 V) and supports up to 64 DALI 1.0 ballasts or DALI 2.0 control gears on a single line. Primary side accepts the same 12–48 V DC supply as the SIO/ETH unit. The gateway and PSU tap into the same D+ / D- pair — the PSU energises the bus while the gateway reads and writes DALI frames.

Three LEDs on the PSU help during fault diagnosis: POWER (primary 12–48 V OK, always on in normal operation), DALI (flashes briefly during DALI bus traffic), ERR (steady on PSU fault, DALI short circuit or bus overload — must be off in normal operation).

DALI bus topology

A DALI line imposes a few hard constraints that directly affect runtime stability:

Conductor cross-sectionMax DALI cable length
1.5 mm²300 m
1.0 mm²238 m
0.75 mm²174 m
0.5 mm²116 m

The limit comes from the maximum permitted 2 V voltage drop across the DALI cable. Other rules:

  • Maximum 64 DALI 1.0 ballasts and up to 64 DALI 2.0 control gears per bus (separate short-address spaces).
  • Tree topology only — no rings, no loops.
  • Actual gear count also depends on the PSU current rating (typically 200 mA on the RESI-DALI-PS; budget ~2 mA per gear plus reserve).

RESI-DALI-PS (or equivalent DALI PSU) is mandatory. Without it the gateway cannot drive the bus and every per-gear read will time out as “Gear not responding!”. If a second PSU is added on a long line, insert a DALI choke or split the bus into galvanically separate segments.

Configuration

Enabling Modbus communication

Host serial parameters on the RESI-DALI-SIO are selected by a 4-position DIP switch on the module. The TapHome template is fixed at 9600 baud 8N1, so the DIP switches must match.

DIP1DIP2Baud rate
OFFOFF9600 bd (required by TapHome)
ONOFF19200 bd
OFFON38400 bd
ONONUses FLASH-programmed baud rate (factory default 57600 bd)
DIP4Modbus Unit ID source
OFFFLASH register H:65221 is honoured — required if a non-default Slave ID was programmed during commissioning
ONHardware fallback — Unit ID is forced to 255, the FLASH value is ignored

DIP3 is a product-specific reserved bit; leave it in the factory position. Parity is fixed at None and stop bits at 1 (8N1 frame format) for the DIP1/DIP2 baud-rate combinations selected above.

RS-485 termination: terminate the bus with 120 Ω at both ends. TapHome Core provides one termination on its BUS terminals; if the RESI gateway is the last device on the line, add a discrete 120 Ω resistor across A+ / B- on its RS-485 terminals.

Slave ID

Factory-default Modbus Unit ID is 255 (FLASH register H:65221 reads 0xFFFF, which the gateway clamps to 255). The template exposes a module-level service attribute Slave ID that reads back this register, so the configured address can be verified after import.

Changing the Slave ID is a commissioning concern handled by the sibling Resi Dali - Configuration template — this runtime template intentionally does not write to H:65221 or trigger a software reset, so it can be left active in the project without risk of accidental gateway reconfiguration.

RTU-over-TCP setup

Because the template uses a Modbus RTU connection, an IpAddress import parameter is also offered for installations where TapHome reaches the gateway through a serial-to-IP bridge (Moxa NPort, USR-TCP232) or through the RESI-DALI-ETH in transparent RTU-over-Ethernet mode. In that case point IpAddress at the bridge / ETH unit IP and keep all other settings identical to the RS-485 variant. For pure Modbus/TCP a TCP-based template would be more natural — this Modbus RTU template specifically expects framed RTU traffic.

Only one Modbus master at a time. Neither DALI nor Modbus RTU arbitrate between concurrent masters. If a PLC, BMS or PC tool (for example MODBUSConfigurator) is also polling the gateway, gear queries will collide and TapHome will report sporadic “Gear not responding!” errors.

Relationship with the commissioning template

The same Dali.xml definition ships with the RESI gateway as two complementary TapHome templates that operate on different lifecycle phases of the same hardware:

TemplatePhaseWhat it doesWhen to use
Resi Dali - Configuration (resi-dali-daliconfig)One-off commissioningDALI 2.0 random-addressing wizard, per-address device-type probe, broadcast ON/OFF sanity tests, Reset / Delete / Change short address, Set Slave ID, Reset GatewayNew installation: assign short addresses 0–63 to control gears, verify which addresses are populated
Resi DALI (resi-dali-dali, this template)Daily runtime controlPer-gear dimming (64 gears), group dimming (16 groups), per-gear DALI 1.0 diagnostics, group membership managementAfter commissioning: end-user lighting control

Typical project flow:

  1. Wire and power the DALI bus (RESI-DALI-PS) and the gateway.
  2. Import the Resi Dali - Configuration template, run the module action Reset Short Addresses (Confirm = Yes), then select Start on the Find New Devices wizard — it steps through Scan Short Address → Set SEARCHADDR → Check → PROGRAM SHORT ADDRESS on its own until it stops on Finish, then close the session with TERMINATE. Verify with Gear 0..63 and Turn On / Off All Lamps.
  3. Remove the Resi Dali - Configuration module from the project.
  4. Import the Resi DALI template (this one) for runtime control. Map the Gear N and Group N devices to TapHome scenes, schedules and rules.

Both templates can also coexist temporarily — e.g. when adding a new gear to a live installation — but only one should send Modbus traffic at a time. The runtime template intentionally omits gateway reconfiguration actions (Reset Gateway, Set Slave ID, broadcast ALL LEVEL) so it can stay in the project without risk of overwriting commissioning settings.

The manufacturer’s free MODBUSConfigurator DCA tool (Windows, available from the RESI website) can also be used for commissioning instead of the Resi Dali - Configuration TapHome template. The runtime Resi DALI template works the same way regardless of which tool was used to assign short addresses.

Device capabilities

The template exposes 80 DALI devices in TapHome, organised in two parallel groups: 64 Gear dimmers (one per DALI short address) and 16 Group dimmers (one per DALI group). One module-level service attribute completes the picture.

Per-gear dimming — Gear 0 … Gear 63

Each of the 64 service devices Gear 0, Gear 1, … Gear 63 is a TapHome dimmer that targets a single DALI control gear at the matching short address. Brightness writes go to H:510 LAMP LEVEL (TapHome 0–100 % is scaled linearly into the DALI arc-power range bounded by the gear’s own MIN LEVEL and MAX LEVEL); the actual level is read back from H:511 via the DALI QUERY ACTUAL LEVEL (0xA0) command and rescaled into 0–100 %.

When the template imports a gear, an init script first probes the gear with QUERY STATUS (0x90); any reply byte proves the gear is present and is stored as its status. A 0x8000 “no answer” response marks the gear as not responding (for a random 1–60 s) and emits “Gear not responding!” in the TapHome log. If the gear answers, the init script then reads QUERY MIN LEVEL (0xA2) and QUERY MAX LEVEL (0xA1) and stores the results in internal state for the periodic readscript.

Every query is guarded against stale and corrupted answers: before each query the template reads away an unread answer left in H:511, so it is not taken for the answer to the new query. A level answer that is not a valid level (0xFF, 0x20FF bus collision, 0x8000 no answer) or lies between 1 and the gear’s MIN LEVEL is rejected and the last level is kept, and a changed level is accepted only after a second equal read. The level service attributes show -- instead of an invalid value.

Each gear exposes 12 service attributes that read DALI 1.0 / 2.0 query commands on demand:

Service attributeDALI commandReports
Short Address(internal)The short address (0–63) targeted by this device — fixed at template-import time
Device TypeQUERY DEVICE TYPE (0x99)DT{n} (e.g. DT0 fluorescent, DT6 LED, DT8 colour / tunable white) or -- when no answer. A gear with several device types answers 0xFF (MASK); the template then lists them with QUERY NEXT DEVICE TYPE (0xA7), e.g. DT6, DT8, or shows MASK when no type is returned
Physical Min LevelQUERY PHYSICAL MIN LEVEL (0x9A)Hardware-imposed minimum dimming level in %
Max LevelQUERY MAX LEVEL (0xA1)Configured upper bound used when scaling 0–100 % to DALI arc power
Min LevelQUERY MIN LEVEL (0xA2)Configured lower bound used when scaling 0–100 % to DALI arc power
Power Up LevelQUERY POWER ON LEVEL (0xA3)Brightness restored after DALI bus power is re-applied
System Failure LevelQUERY SYSTEM FAILURE LEVEL (0xA4)Brightness adopted when the bus loses communication for >500 ms
GroupsQUERY GROUPS 0-7 (0xC0) + QUERY GROUPS 8-15 (0xC1)Comma-separated list of DALI groups the gear belongs to (None or e.g. 0,3,7)
DALI StatusQUERY STATUS (0x90)Decoded status byte with its raw value: OK when no bit is set, otherwise the set flags — gear failure, lamp failure, lamp on, limit error, fade running, reset state, missing short address, power cycle — e.g. lamp on (0x04). A failure present since the gear’s first answer is marked not raised: … reported since start
FadeQUERY FADE TIME/FADE RATE (0xA5) + QUERY EXTENDED FADE TIME (0xA8)fade time {n} ({t} s), fade rate {n}; fade time off when it is 0, or extended 0x.. when the gear uses an extended fade time instead
DALI VersionQUERY VERSION NUMBER (0x97)DALI-1 or DALI-2 {major}.{minor}, followed by the raw value in brackets
Bus statistics(internal counters)Counters since start: queries, stale answers read away, no answer, collisions, rejected levels

The periodic readscript on each gear reads the current arc level (0xA0) and polls QUERY STATUS (0x90) at least once every 60 read cycles. Bit 0 of the status byte raises “Control gear failure!” and bit 1 raises “Lamp failure!” — surfaced as TapHome warnings/errors so a failing fluorescent tube or a tripped LED driver shows up in the activity log without manual polling. A changed failure bit is taken only after a second equal read, and a lost answer is asked once more before the gear is reported as “Gear not responding!”. Failure bits that a gear reports in its first answer are treated as its permanent state: they are shown in DALI Status but not raised, until the bit clears — a later failure is then raised again. Power failure is not raised as an error; the status bit appears in DALI Status as power cycle.

Per-gear configuration actions

Five service actions on every Gear N device write DALI configuration commands (auto-repeated within 100 ms via H:512 LAMP COMMAND + REPEAT as required for non-volatile DALI commands):

  • Add to Group (parameter Group ID 0–15) — sends ADD TO GROUP (0x60+grp) so the gear joins the chosen DALI group and will obey subsequent broadcast group writes.
  • Remove from Group (parameter Group ID 0–15) — sends REMOVE FROM GROUP (0x70+grp).
  • Reset to Default Settings — sends DALI RESET (0x20); restores all NVM parameters of the gear (max/min/power-on/system-failure level, group membership, scenes) to the manufacturer default.
  • Set Startup Levels — captures the current arc power and stores it as Power On Level and System Failure Level, in sequence: STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D), each with a 10 ms gap.
  • Disable fading (no parameters) — loads 0 into the DALI data transfer register (0xA300 written to H:541) and stores it in the gear as fade time (0x2E) and extended fade time (0x30), each with a 100 ms gap, so level changes take effect without fading. The current values are shown in the Fade attribute.

Group dimming — Group 0 … Group 15

Each of the 16 service devices Group 0, Group 1, … Group 15 is a write-only TapHome dimmer that broadcasts a single arc-power value to every gear belonging to that DALI group, using H:520 GROUP LEVEL (TapHome 0–100 % is multiplied by 0xFE → DALI arc power 0–254). DALI does not let the gateway read back a group level — the bus only carries the broadcast write — so TapHome holds the last written value as the “current” level. There are no init or read scripts on group devices.

Each Group N device exposes two service actions for bulk membership management:

  • Add All Lamps to Group — broadcasts ADD TO GROUP (0x60+grp) via H:532 ALL COMMAND + REPEAT. Every gear on the bus joins the group regardless of its current short address.
  • Remove All Lamps from Group — broadcasts REMOVE FROM GROUP (0x70+grp). Every gear leaves the group.

Use group dimming for “kitchen lights”, “living-room downlights” or any zone where multiple gears should track a single dimmer. Group writes are far more efficient than driving individual gears in parallel — one Modbus transaction reaches up to 64 gears simultaneously.

Module-level diagnostics

The module exposes a single service attribute:

  • Slave ID — reads the Modbus Unit ID stored in FLASH register H:65221. Active only when DIP4 = OFF; with DIP4 = ON the gateway forces address 255 regardless of the FLASH value. Use this to verify the gateway address before adding more devices, especially after a commissioning round that wrote a non-default ID.

Troubleshooting

“Gear not responding!” on a known-addressed gear

  1. Confirm DALI bus voltage at the gear: 11.5–22.5 V (typical 14–16 V). Lower readings usually indicate a short circuit or a bus that is too long for the cable cross-section (see DALI bus topology).
  2. Check the ERR LED on the RESI-DALI-PS — if lit, the bus is shorted, overloaded or the PSU itself is faulty.
  3. Re-run Gear N probing in the sibling Resi Dali - Configuration template — the runtime template only retries after a random pause of up to 60 s, the commissioning template gives an immediate pass/fail per address.
  4. Verify the gear actually has a short address. If it was replaced or factory-reset on the bus, it will be un-addressed and must be re-commissioned via Resi Dali - Configuration.

Group dimming has no effect

  1. Confirm at least one gear is a member of the group — open Gear N → Groups on each suspected member and check that the group ID appears in the comma-separated list.
  2. If membership is missing, fire the Add to Group action on each gear individually, or use Add All Lamps to Group on the group device for a bus-wide broadcast.
  3. Remember that Group N is write-only — the dimmer slider in TapHome stores the last written value, but does not reflect the actual bus state. If gears are powered off, the group level command silently has no effect on them.

Lamp failure / power failure not reported

  1. Open the gear’s DALI Status attribute. If it ends with not raised: … reported since start, the failure was already present in the gear’s first answer and is treated as its permanent state — it is shown but not raised until the bit clears once.
  2. Power failure is not raised as an error at all; the status bit is shown in DALI Status as power cycle.
  3. A new failure appears only after the status is polled (at least once every 60 read cycles) and read twice the same, so a short flicker of the failure bit is not reported.

Sporadic Modbus errors after commissioning

  1. Make sure no other Modbus master (MODBUSConfigurator, PLC, BMS) is polling the gateway concurrently. DALI and Modbus do not arbitrate between masters. The Bus statistics attribute on each gear counts stale answers read away, missing answers, collisions and rejected levels since start — counts that keep rising point to concurrent traffic on the gateway or the DALI bus.
  2. Verify RS485 GND is connected between the gateway and the TapHome power supply common — missing GND is the most common RS-485 fault on long lines.
  3. For RTU-over-TCP through a serial bridge, confirm the bridge runs in framed RTU mode (not raw TCP socket) and that the inter-character gap on the serial side respects 9600 baud 8N1 timing.
  4. If the symptoms appeared after changing the Slave ID with the Resi Dali - Configuration template, re-import this runtime template with the new SlaveId import parameter — the previously imported module is still polling at the old address.

Available devices 80

These devices appear in TapHome after import.

Resi DALI Module
Service Attributes
Slave IDReads the Modbus Unit ID stored in FLASH register H:65221 — active only when DIP4=OFF (factory default 255). Use to verify the gateway address before adding more devices.

Resi Dali

Service Attributes
${xml_slave_id}
modbusr(H,65221,uint16)
Gear 0 Dimmer Read and write

DALI control gear at short address 0 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 0

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 1 Dimmer Read and write

DALI control gear at short address 1 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 1

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 2 Dimmer Read and write

DALI control gear at short address 2 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 2

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 3 Dimmer Read and write

DALI control gear at short address 3 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 3

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 4 Dimmer Read and write

DALI control gear at short address 4 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 4

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 5 Dimmer Read and write

DALI control gear at short address 5 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 5

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 6 Dimmer Read and write

DALI control gear at short address 6 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 6

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 7 Dimmer Read and write

DALI control gear at short address 7 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 7

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 8 Dimmer Read and write

DALI control gear at short address 8 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 8

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 9 Dimmer Read and write

DALI control gear at short address 9 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 9

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 10 Dimmer Read and write

DALI control gear at short address 10 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 10

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 11 Dimmer Read and write

DALI control gear at short address 11 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 11

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 12 Dimmer Read and write

DALI control gear at short address 12 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 12

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 13 Dimmer Read and write

DALI control gear at short address 13 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 13

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 14 Dimmer Read and write

DALI control gear at short address 14 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 14

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 15 Dimmer Read and write

DALI control gear at short address 15 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 15

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 16 Dimmer Read and write

DALI control gear at short address 16 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 16

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 17 Dimmer Read and write

DALI control gear at short address 17 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 17

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 18 Dimmer Read and write

DALI control gear at short address 18 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 18

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 19 Dimmer Read and write

DALI control gear at short address 19 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 19

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 20 Dimmer Read and write

DALI control gear at short address 20 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 20

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 21 Dimmer Read and write

DALI control gear at short address 21 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 21

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 22 Dimmer Read and write

DALI control gear at short address 22 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 22

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 23 Dimmer Read and write

DALI control gear at short address 23 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 23

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 24 Dimmer Read and write

DALI control gear at short address 24 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 24

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 25 Dimmer Read and write

DALI control gear at short address 25 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 25

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 26 Dimmer Read and write

DALI control gear at short address 26 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 26

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 27 Dimmer Read and write

DALI control gear at short address 27 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 27

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 28 Dimmer Read and write

DALI control gear at short address 28 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 28

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 29 Dimmer Read and write

DALI control gear at short address 29 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 29

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 30 Dimmer Read and write

DALI control gear at short address 30 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 30

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 31 Dimmer Read and write

DALI control gear at short address 31 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 31

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 32 Dimmer Read and write

DALI control gear at short address 32 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 32

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 33 Dimmer Read and write

DALI control gear at short address 33 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 33

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 34 Dimmer Read and write

DALI control gear at short address 34 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 34

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 35 Dimmer Read and write

DALI control gear at short address 35 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 35

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 36 Dimmer Read and write

DALI control gear at short address 36 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 36

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 37 Dimmer Read and write

DALI control gear at short address 37 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 37

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 38 Dimmer Read and write

DALI control gear at short address 38 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 38

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 39 Dimmer Read and write

DALI control gear at short address 39 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 39

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 40 Dimmer Read and write

DALI control gear at short address 40 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 40

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 41 Dimmer Read and write

DALI control gear at short address 41 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 41

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 42 Dimmer Read and write

DALI control gear at short address 42 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 42

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 43 Dimmer Read and write

DALI control gear at short address 43 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 43

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 44 Dimmer Read and write

DALI control gear at short address 44 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 44

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 45 Dimmer Read and write

DALI control gear at short address 45 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 45

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 46 Dimmer Read and write

DALI control gear at short address 46 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 46

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 47 Dimmer Read and write

DALI control gear at short address 47 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 47

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 48 Dimmer Read and write

DALI control gear at short address 48 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 48

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 49 Dimmer Read and write

DALI control gear at short address 49 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 49

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 50 Dimmer Read and write

DALI control gear at short address 50 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 50

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 51 Dimmer Read and write

DALI control gear at short address 51 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 51

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 52 Dimmer Read and write

DALI control gear at short address 52 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 52

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 53 Dimmer Read and write

DALI control gear at short address 53 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 53

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 54 Dimmer Read and write

DALI control gear at short address 54 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 54

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 55 Dimmer Read and write

DALI control gear at short address 55 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 55

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 56 Dimmer Read and write

DALI control gear at short address 56 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 56

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 57 Dimmer Read and write

DALI control gear at short address 57 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 57

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 58 Dimmer Read and write

DALI control gear at short address 58 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 58

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 59 Dimmer Read and write

DALI control gear at short address 59 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 59

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 60 Dimmer Read and write

DALI control gear at short address 60 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 60

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 61 Dimmer Read and write

DALI control gear at short address 61 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 61

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 62 Dimmer Read and write

DALI control gear at short address 62 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 62

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Gear 63 Dimmer Read and write

DALI control gear at short address 63 — TapHome dimmer mapped to DALI arc power 0–100 % via H:510 LAMP LEVEL (write) and QUERY ACTUAL LEVEL via H:511 (read), bounded by the gear's own MIN / MAX LEVEL.

Register: H:511 → H:510 UInt16 Unit: % numeric
Service Attributes
Short AddressDALI short address (0–63) targeted by this device — fixed at template-import time and used in the upper byte of every Modbus write.
Device TypeDALI device-type byte returned by QUERY DEVICE TYPE (0x99) — reports DT0 (fluorescent), DT6 (LED driver), DT8 (colour / tunable white) etc., or -- when no answer.
Physical Min LevelHardware-imposed minimum dimming level reported by the gear — read from QUERY PHYSICAL MIN LEVEL (0x9A), as a percentage.
Max LevelConfigured DALI MAX LEVEL — read from QUERY MAX LEVEL (0xA1); used as the upper bound when scaling TapHome 0–100 % to DALI arc power.
Min LevelConfigured DALI MIN LEVEL — read from QUERY MIN LEVEL (0xA2); used as the lower bound when scaling TapHome 0–100 % to DALI arc power.
Power Up LevelBrightness restored after DALI bus power is recovered — read from QUERY POWER ON LEVEL (0xA3).
System Failure LevelBrightness adopted when the DALI bus loses communication for more than 500 ms — read from QUERY SYSTEM FAILURE LEVEL (0xA4).
GroupsComma-separated list of DALI groups (0–15) the gear belongs to — decoded from QUERY GROUPS 0-7 (0xC0) and QUERY GROUPS 8-15 (0xC1); shows None or e.g. 0,3,7.
Support Lamp FailureWhether the lamp / driver supports lamp-failure detection — Yes / No / --. Gates the Lamp failure! error in the periodic readscript.
Support Power FailureWhether the lamp / driver supports power-failure detection — Yes / No / --. Gates the Power failure! error in the periodic readscript.
DALI Version
Bus statistics
Service Actions
Add to GroupAdds this gear to a DALI group (0–15) via ADD TO GROUP (0x60+grp) — auto-repeated within 100 ms as required for DALI configuration commands.
Remove from GroupRemoves this gear from a DALI group (0–15) via REMOVE FROM GROUP (0x70+grp).
Reset to Default SettingsSends DALI RESET (0x20) — restores every NVM parameter of the gear (max / min / power-on / system-failure level, group membership, scenes) to the manufacturer default.
Set Startup LevelsCaptures the current arc power and stores it as Power On Level and System Failure Level — sequence STORE ACTUAL LEVEL IN DTR (0x21) → STORE DTR AS POWER ON LEVEL (0x2C) → STORE DTR AS SYSTEM FAILURE LEVEL (0x2D).
Disable fading

Gear 63

Read (module)
var x := 0; var rx := 0; var y := 0;
if Status > 0
    Status := Status - 1;
    adderror(3, "Gear not responding!");
    return(0);
end

# checkStatus: ReadLevel resets it while the gear answers; statusPoll: QUERY STATUS at least once per 60 runs
checkStatus := checkStatus - 1;
statusPoll := statusPoll - 1;
if checkStatus < 1 or statusPoll < 1
    # an unread answer left in the gateway would be taken for this query: read it away and count it
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    # a single lost answer happens on DALI: ask once more before declaring the gear missing
    if x = 0x8000
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    end
    if x <= 0xFF
        # a changed failure bit must be read twice the same before it is taken
        if statusSeen = 1 and getbits(x, 0, 2) != getbits(gearStatus, 0, 2)
            y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
            modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
            if y != x
                cntBad := cntBad + 1;
                x := gearStatus;
            end
        end
        if statusSeen = 0
            ignGear := getbit(x, 0);
            ignLamp := getbit(x, 1);
            statusSeen := 1;
        end
        # a bit that clears ends its ignore, so a later failure is raised
        if (getbit(x, 0) = 0, ignGear := 0);
        if (getbit(x, 1) = 0, ignLamp := 0);
        gearStatus := x;
        checkStatus := 60;
        statusPoll := 60;
    else
        if x = 0x8000
            Status := RANDINT(10, 60);
            adderror(3, "Gear not responding!");
            return(0);
        end
    end
end

# messages last one run only, so they are raised from the stored status on every run
if ((getbit(gearStatus, 0) = 1) and (ignGear = 0), adderror(1, "Control gear failure!"));
if ((getbit(gearStatus, 1) = 1) and (ignLamp = 0), adderror(2, "Lamp failure!"));
Initialize
var x := 0; var rx := 0;
# QUERY STATUS 0x90: every reply byte proves the gear is present; 0x8000 = no answer, 0x20FF = bus collision
# an unread answer left in the gateway would be taken for this query: read it away and count it
x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
x := modbusr(SH, 511, uint16);
cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
statusSeen := 0;
if x <= 0xFF
    # failure bits a gear reports from its first answer are its permanent state, not an event: shown, not raised
    gearStatus := x;
    ignGear := getbit(x, 0);
    ignLamp := getbit(x, 1);
    statusSeen := 1;
    Status := 0;
else
    Status := RANDINT(1, 60);
end

if Status = 0
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, minLevel := x);

    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x <= 0xFE, maxLevel := x);
end
Read level
var x := 0; var rx := 0; var y := 0;
if Status = 0
    if minLevel = 0
        # an unread answer left in the gateway would be taken for this query: read it away and count it
        x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        x := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
        if (x <= 0xFE, minLevel := x);
    end
    x := modbusr(SH, 511, uint16); if (x != 0x8000, cntStale := cntStale + 1);
    modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    cntQ := cntQ + 1; if (x = 0x8000, cntNone := cntNone + 1); if (x = 0x20FF, cntColl := cntColl + 1);
    if (x != 0x8000 or rx, checkStatus := 60);
    # 0xFF = MASK, 0x20FF = collision, 0x8000 = no answer: keep the last level instead of a phantom value
    if x > 0xFE
        return(Le);
    end
    # an actual level is 0 or between the gear's min and max: anything else is not this gear's answer
    if x > 0 and x < minLevel
        cntBad := cntBad + 1;
        return(Le);
    end
    # a changed level must be read twice the same before it is taken
    if x != lastRaw
        y := modbusr(SH, 511, uint16); if (y != 0x8000, cntStale := cntStale + 1);
        modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA0);
        sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        y := modbusr(SH, 511, uint16);
        cntQ := cntQ + 1; if (y = 0x8000, cntNone := cntNone + 1); if (y = 0x20FF, cntColl := cntColl + 1);
        if y != x
            cntBad := cntBad + 1;
            return(Le);
        end
        lastRaw := x;
    end
    return(if(x = 0, 0, linear(x, minLevel, 0.01, maxLevel, 1)));
else
    return(NaN);
end
Write level
var level := 0;
if(Le > 0, level := round(linear(Le, 0.01, minLevel, 1, maxLevel)));
modbusw(SH, 510, uint16, (ShortAddress << 8) + level );
Service Attributes
${xml_short_address}
ShortAddress
${xml_device_type}
var x := 0; var y := 0; var rx := 0; var s := ""; var i := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x99);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    x := modbusr(SH, 511, uint16);
    if (x < 0xFF, return("DT" + x));
    if x = 0xFF
        while i < 8
            modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA7);
            sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
            y := modbusr(SH, 511, uint16);
            if y < 0xFE
                s := if(s = "", "DT" + y, s + ", DT" + y);
                i := i + 1;
            else
                i := 8;
            end
        loop
        return(if(s = "", "MASK", s));
    end
end

return("--")
Physical Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x9A);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Max Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA1);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Min Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA2);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
Power Up Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA3);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
System Failure Level
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA4);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if (x <= 0xFE, return(round((x / 0xFE) * 100) + " %"));
end

return("--")
${xml_groups}
var x := 0; var rx := 0;
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC0);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g1 := modbusr(SH, 511, uint16);
    if (g1 > 0xFF, return("--")); 

    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xC1);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var g2 := modbusr(SH, 511, uint16);
    if (g2 > 0xFF, return("--")); 

    var ret := "";

    if (getbit(g1, 0), ret := ret + "0");
    if (getbit(g1, 1), if (ret = "", ret := "1", ret := ret + ",1"));
    if (getbit(g1, 2), if (ret = "", ret := "2", ret := ret + ",2"));
    if (getbit(g1, 3), if (ret = "", ret := "3", ret := ret + ",3"));
    if (getbit(g1, 4), if (ret = "", ret := "4", ret := ret + ",4"));
    if (getbit(g1, 5), if (ret = "", ret := "5", ret := ret + ",5"));
    if (getbit(g1, 6), if (ret = "", ret := "6", ret := ret + ",6"));
    if (getbit(g1, 7), if (ret = "", ret := "7", ret := ret + ",7"));
    if (getbit(g2, 0), if (ret = "", ret := "8", ret := ret + ",8"));
    if (getbit(g2, 1), if (ret = "", ret := "9", ret := ret + ",9"));
    if (getbit(g2, 2), if (ret = "", ret := "10", ret := ret + ",10"));
    if (getbit(g2, 3), if (ret = "", ret := "11", ret := ret + ",11"));
    if (getbit(g2, 4), if (ret = "", ret := "12", ret := ret + ",12"));
    if (getbit(g2, 5), if (ret = "", ret := "13", ret := ret + ",13"));
    if (getbit(g2, 6), if (ret = "", ret := "14", ret := ret + ",14"));
    if (getbit(g2, 7), if (ret = "", ret := "15", ret := ret + ",15"));

    if (ret = "", return("None"), return(ret));
else
    return("--");
end
DALI Status
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x90);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var s := "";
        if (getbit(x, 0), s := if(s = "", "gear failure", s + ", gear failure"));
        if (getbit(x, 1), s := if(s = "", "lamp failure", s + ", lamp failure"));
        if (getbit(x, 2), s := if(s = "", "lamp on", s + ", lamp on"));
        if (getbit(x, 3), s := if(s = "", "limit error", s + ", limit error"));
        if (getbit(x, 4), s := if(s = "", "fade running", s + ", fade running"));
        if (getbit(x, 5), s := if(s = "", "reset state", s + ", reset state"));
        if (getbit(x, 6), s := if(s = "", "missing short address", s + ", missing short address"));
        if (getbit(x, 7), s := if(s = "", "power cycle", s + ", power cycle"));
        var ign := if((ignGear = 1) and (ignLamp = 1), "gear and lamp failure", if(ignGear = 1, "gear failure", if(ignLamp = 1, "lamp failure", "")));
        return(if(s = "", "OK", s) + " (0x" + tostring(x, "X2") + ")" + if(ign = "", "", "; not raised: " + ign + " reported since start"));
    end
end

return("--")
Fade
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA5);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        var t := getbits(x, 4, 4);
        var ret := if(t = 0, "off", t + " (" + round(sqrt(power(2, t)) * 5) / 10 + " s)");
        modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0xA8);
    sleep(5); rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
        var e := modbusr(SH, 511, uint16);
        if (t = 0 and e <= 0xFF and e != 0, ret := "extended 0x" + tostring(e, "X2"));
        return("fade time " + ret + ", fade rate " + getbits(x, 0, 4));
    end
end

return("--")
DALI Version
if Status = 0
    modbusr(SH, 511, uint16); modbusw(SH, 511, uint16, (ShortAddress << 8) + 0x97);
    sleep(5); var rx := getbits(modbusr(SH, 550, uint16), 0, 2); if(rx, 0, sleep(50));
    var x := modbusr(SH, 511, uint16);
    if x <= 0xFF
        return(if(x >= 8, "DALI-2 " + getbits(x, 2, 6) + "." + getbits(x, 0, 2), "DALI-1") + " (" + x + ")");
    end
end

return("--")
Bus statistics
return("since start: queries " + cntQ + ", stale answers read away " + cntStale + ", no answer " + cntNone + ", collisions " + cntColl + ", rejected levels " + cntBad)
Service Actions
${xml_add_to_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x60 + grp);
${xml_remove_from_group}
Parameters: Group ID (0–15 grp)
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x70 + grp);
${xml_reset_to_default_settings}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x20);
${xml_set_startup_levels}
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x21);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2c);
sleep(10);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2d);
Disable fading
modbusw(SH, 541, uint16, 0xA300);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x2E);
sleep(100);
modbusw(SH, 512, uint16, (ShortAddress << 8) + 0x30);
Group 0 Dimmer Read and write

DALI broadcast group 0 (0–15) — write-only TapHome dimmer that broadcasts a single arc-power value to every gear in this group via H:520 GROUP LEVEL. The bus does not echo group levels, so TapHome holds the last written value.

UInt16 Unit: % numeric
Service Actions
Add All Lamps to GroupBroadcasts ADD TO GROUP via H:532 ALL COMMAND + REPEAT — every gear on the bus joins this group regardless of its current short address.
Remove All Lamps from GroupBroadcasts REMOVE FROM GROUP via H:532 ALL COMMAND + REPEAT — every gear on the bus leaves this group.

Group 0

Write level
var GroupAddress := 0;
modbusw(SH, 520, uint16, (GroupAddress << 8) + (Le * 0xFE));
Service Actions
${xml_add_to_group_all_lamps}
var GroupAddress := 0;
modbusw(SH, 532, uint16, 0x60 + GroupAddress)
${xml_remove_from_group_all_lamps}
var GroupAddress := 0;
modbusw(SH, 532, uint16, 0x70 + GroupAddress)
Group 1 Dimmer Read and write

DALI broadcast group 1 (0–15) — write-only TapHome dimmer that broadcasts a single arc-power value to every gear in this group via H:520 GROUP LEVEL. The bus does not echo group levels, so TapHome holds the last written value.

UInt16 Unit: % numeric
Service Actions
Add All Lamps to GroupBroadcasts ADD TO GROUP via H:532 ALL COMMAND + REPEAT — every gear on the bus joins this group regardless of its current short address.
Remove All Lamps from GroupBroadcasts REMOVE FROM GROUP via H:532 ALL COMMAND + REPEAT — every gear on the bus leaves this group.

Group 1

Write level
var GroupAddress := 1;
modbusw(SH, 520, uint16, (GroupAddress << 8) + (Le * 0xFE));
Service Actions
${xml_add_to_group_all_lamps}
var GroupAddress := 1;
modbusw(SH, 532, uint16, 0x60 + GroupAddress)
${xml_remove_from_group_all_lamps}
var GroupAddress := 1;
modbusw(SH, 532, uint16, 0x70 + GroupAddress)
Group 2 Dimmer Read and write

DALI broadcast group 2 (0–15) — write-only TapHome dimmer that broadcasts a single arc-power value to every gear in this group via H:520 GROUP LEVEL. The bus does not echo group levels, so TapHome holds the last written value.

UInt16 Unit: % numeric
Service Actions
Add All Lamps to GroupBroadcasts ADD TO GROUP via H:532 ALL COMMAND + REPEAT — every gear on the bus joins this group regardless of its current short address.
Remove All Lamps from GroupBroadcasts REMOVE FROM GROUP via H:532 ALL COMMAND + REPEAT — every gear on the bus leaves this group.

Group 2

Write level
var GroupAddress := 2;
modbusw(SH, 520, uint16, (GroupAddress << 8) + (Le * 0xFE));
Service Actions
${xml_add_to_group_all_lamps}
var GroupAddress := 2;
modbusw(SH, 532, uint16, 0x60 + GroupAddress)
${xml_remove_from_group_all_lamps}
var GroupAddress := 2;
modbusw(SH, 532, uint16, 0x70 + GroupAddress)
Group 3 Dimmer Read and write

DALI broadcast group 3 (0–15) — write-only TapHome dimmer that broadcasts a single arc-power value to every gear in this group via H:520 GROUP LEVEL. The bus does not echo group levels, so TapHome holds the last written value.

UInt16 Unit: % numeric
Service Actions
Add All Lamps to GroupBroadcasts ADD TO GROUP via H:532 ALL COMMAND + REPEAT — every gear on the bus joins this group regardless of its current short address.
Remove All Lamps from GroupBroadcasts REMOVE FROM GROUP via H:532 ALL COMMAND + REPEAT — every gear on the bus leaves this group.

Group 3

Write level
var GroupAddress := 3;
modbusw(SH, 520, uint16, (GroupAddress << 8) + (Le * 0xFE));
Service Actions
${xml_add_to_group_all_lamps}
var GroupAddress := 3;
modbusw(SH, 532, uint16, 0x60 + GroupAddress)
${xml_remove_from_group_all_lamps}
var GroupAddress := 3;
modbusw(SH, 532, uint16, 0x70 + GroupAddress)
Group 4 Dimmer Read and write

DALI broadcast group 4 (0–15) — write-only TapHome dimmer that broadcasts a single arc-power value to every gear in this group via H:520 GROUP LEVEL. The bus does not echo group levels, so TapHome holds the last written value.

UInt16 Unit: % numeric
Service Actions
Add All Lamps to GroupBroadcasts ADD TO GROUP via H:532 ALL COMMAND + REPEAT — every gear on the bus joins this group regardless of its current short address.
Remove All Lamps from GroupBroadcasts REMOVE FROM GROUP via H:532 ALL COMMAND + REPEAT — every gear on the bus leaves this group.

Group 4

Write level
var GroupAddress := 4;
modbusw(SH, 520, uint16, (GroupAddress << 8) + (Le * 0xFE));
Service Actions
${xml_add_to_group_all_lamps}
var GroupAddress := 4;
modbusw(SH, 532, uint16, 0x60 + GroupAddress)
${xml_remove_from_group_all_lamps}
var GroupAddress := 4;
modbusw(SH, 532, uint16, 0x70 + GroupAddress)
Group 5 Dimmer Read and write

DALI broadcast group 5 (0–15) — write-only TapHome dimmer that broadcasts a single arc-power value to every gear in this group via H:520 GROUP LEVEL. The bus does not echo group levels, so TapHome holds the last written value.

UInt16 Unit: % numeric
Service Actions
Add All Lamps to GroupBroadcasts ADD TO GROUP via H:532 ALL COMMAND + REPEAT — every gear on the bus joins this group regardless of its current short address.
Remove All Lamps from GroupBroadcasts REMOVE FROM GROUP via H:532 ALL COMMAND + REPEAT — every gear on the bus leaves this group.

Group 5

Write level
var GroupAddress := 5;
modbusw(SH, 520, uint16, (GroupAddress << 8) + (Le * 0xFE));
Service Actions
${xml_add_to_group_all_lamps}
var GroupAddress := 5;
modbusw(SH, 532, uint16, 0x60 + GroupAddress)
${xml_remove_from_group_all_lamps}
var GroupAddress := 5;
modbusw(SH, 532, uint16, 0x70 + GroupAddress)
Group 6 Dimmer Read and write

DALI broadcast group 6 (0–15) — write-only TapHome dimmer that broadcasts a single arc-power value to every gear in this group via H:520 GROUP LEVEL. The bus does not echo group levels, so TapHome holds the last written value.

UInt16 Unit: % numeric
Service Actions
Add All Lamps to GroupBroadcasts ADD TO GROUP via H:532 ALL COMMAND + REPEAT — every gear on the bus joins this group regardless of its current short address.
Remove All Lamps from GroupBroadcasts REMOVE FROM GROUP via H:532 ALL COMMAND + REPEAT — every gear on the bus leaves this group.

Group 6

Write level
var GroupAddress := 6;
modbusw(SH, 520, uint16, (GroupAddress << 8) + (Le * 0xFE));
Service Actions
${xml_add_to_group_all_lamps}
var GroupAddress := 6;
modbusw(SH, 532, uint16, 0x60 + GroupAddress)
${xml_remove_from_group_all_lamps}
var GroupAddress := 6;
modbusw(SH, 532, uint16, 0x70 + GroupAddress)
Group 7 Dimmer Read and write

DALI broadcast group 7 (0–15) — write-only TapHome dimmer that broadcasts a single arc-power value to every gear in this group via H:520 GROUP LEVEL. The bus does not echo group levels, so TapHome holds the last written value.

UInt16 Unit: % numeric
Service Actions
Add All Lamps to GroupBroadcasts ADD TO GROUP via H:532 ALL COMMAND + REPEAT — every gear on the bus joins this group regardless of its current short address.
Remove All Lamps from GroupBroadcasts REMOVE FROM GROUP via H:532 ALL COMMAND + REPEAT — every gear on the bus leaves this group.

Group 7

Write level
var GroupAddress := 7;
modbusw(SH, 520, uint16, (GroupAddress << 8) + (Le * 0xFE));
Service Actions
${xml_add_to_group_all_lamps}
var GroupAddress := 7;
modbusw(SH, 532, uint16, 0x60 + GroupAddress)
${xml_remove_from_group_all_lamps}
var GroupAddress := 7;
modbusw(SH, 532, uint16, 0x70 + GroupAddress)
Group 8 Dimmer Read and write

DALI broadcast group 8 (0–15) — write-only TapHome dimmer that broadcasts a single arc-power value to every gear in this group via H:520 GROUP LEVEL. The bus does not echo group levels, so TapHome holds the last written value.

UInt16 Unit: % numeric
Service Actions
Add All Lamps to GroupBroadcasts ADD TO GROUP via H:532 ALL COMMAND + REPEAT — every gear on the bus joins this group regardless of its current short address.
Remove All Lamps from GroupBroadcasts REMOVE FROM GROUP via H:532 ALL COMMAND + REPEAT — every gear on the bus leaves this group.

Group 8

Write level
var GroupAddress := 8;
modbusw(SH, 520, uint16, (GroupAddress << 8) + (Le * 0xFE));
Service Actions
${xml_add_to_group_all_lamps}
var GroupAddress := 8;
modbusw(SH, 532, uint16, 0x60 + GroupAddress)
${xml_remove_from_group_all_lamps}
var GroupAddress := 8;
modbusw(SH, 532, uint16, 0x70 + GroupAddress)
Group 9 Dimmer Read and write

DALI broadcast group 9 (0–15) — write-only TapHome dimmer that broadcasts a single arc-power value to every gear in this group via H:520 GROUP LEVEL. The bus does not echo group levels, so TapHome holds the last written value.

UInt16 Unit: % numeric
Service Actions
Add All Lamps to GroupBroadcasts ADD TO GROUP via H:532 ALL COMMAND + REPEAT — every gear on the bus joins this group regardless of its current short address.
Remove All Lamps from GroupBroadcasts REMOVE FROM GROUP via H:532 ALL COMMAND + REPEAT — every gear on the bus leaves this group.

Group 9

Write level
var GroupAddress := 9;
modbusw(SH, 520, uint16, (GroupAddress << 8) + (Le * 0xFE));
Service Actions
${xml_add_to_group_all_lamps}
var GroupAddress := 9;
modbusw(SH, 532, uint16, 0x60 + GroupAddress)
${xml_remove_from_group_all_lamps}
var GroupAddress := 9;
modbusw(SH, 532, uint16, 0x70 + GroupAddress)
Group 10 Dimmer Read and write

DALI broadcast group 10 (0–15) — write-only TapHome dimmer that broadcasts a single arc-power value to every gear in this group via H:520 GROUP LEVEL. The bus does not echo group levels, so TapHome holds the last written value.

UInt16 Unit: % numeric
Service Actions
Add All Lamps to GroupBroadcasts ADD TO GROUP via H:532 ALL COMMAND + REPEAT — every gear on the bus joins this group regardless of its current short address.
Remove All Lamps from GroupBroadcasts REMOVE FROM GROUP via H:532 ALL COMMAND + REPEAT — every gear on the bus leaves this group.

Group 10

Write level
var GroupAddress := 10;
modbusw(SH, 520, uint16, (GroupAddress << 8) + (Le * 0xFE));
Service Actions
${xml_add_to_group_all_lamps}
var GroupAddress := 10;
modbusw(SH, 532, uint16, 0x60 + GroupAddress)
${xml_remove_from_group_all_lamps}
var GroupAddress := 10;
modbusw(SH, 532, uint16, 0x70 + GroupAddress)
Group 11 Dimmer Read and write

DALI broadcast group 11 (0–15) — write-only TapHome dimmer that broadcasts a single arc-power value to every gear in this group via H:520 GROUP LEVEL. The bus does not echo group levels, so TapHome holds the last written value.

UInt16 Unit: % numeric
Service Actions
Add All Lamps to GroupBroadcasts ADD TO GROUP via H:532 ALL COMMAND + REPEAT — every gear on the bus joins this group regardless of its current short address.
Remove All Lamps from GroupBroadcasts REMOVE FROM GROUP via H:532 ALL COMMAND + REPEAT — every gear on the bus leaves this group.

Group 11

Write level
var GroupAddress := 11;
modbusw(SH, 520, uint16, (GroupAddress << 8) + (Le * 0xFE));
Service Actions
${xml_add_to_group_all_lamps}
var GroupAddress := 11;
modbusw(SH, 532, uint16, 0x60 + GroupAddress)
${xml_remove_from_group_all_lamps}
var GroupAddress := 11;
modbusw(SH, 532, uint16, 0x70 + GroupAddress)
Group 12 Dimmer Read and write

DALI broadcast group 12 (0–15) — write-only TapHome dimmer that broadcasts a single arc-power value to every gear in this group via H:520 GROUP LEVEL. The bus does not echo group levels, so TapHome holds the last written value.

UInt16 Unit: % numeric
Service Actions
Add All Lamps to GroupBroadcasts ADD TO GROUP via H:532 ALL COMMAND + REPEAT — every gear on the bus joins this group regardless of its current short address.
Remove All Lamps from GroupBroadcasts REMOVE FROM GROUP via H:532 ALL COMMAND + REPEAT — every gear on the bus leaves this group.

Group 12

Write level
var GroupAddress := 12;
modbusw(SH, 520, uint16, (GroupAddress << 8) + (Le * 0xFE));
Service Actions
${xml_add_to_group_all_lamps}
var GroupAddress := 12;
modbusw(SH, 532, uint16, 0x60 + GroupAddress)
${xml_remove_from_group_all_lamps}
var GroupAddress := 12;
modbusw(SH, 532, uint16, 0x70 + GroupAddress)
Group 13 Dimmer Read and write

DALI broadcast group 13 (0–15) — write-only TapHome dimmer that broadcasts a single arc-power value to every gear in this group via H:520 GROUP LEVEL. The bus does not echo group levels, so TapHome holds the last written value.

UInt16 Unit: % numeric
Service Actions
Add All Lamps to GroupBroadcasts ADD TO GROUP via H:532 ALL COMMAND + REPEAT — every gear on the bus joins this group regardless of its current short address.
Remove All Lamps from GroupBroadcasts REMOVE FROM GROUP via H:532 ALL COMMAND + REPEAT — every gear on the bus leaves this group.

Group 13

Write level
var GroupAddress := 13;
modbusw(SH, 520, uint16, (GroupAddress << 8) + (Le * 0xFE));
Service Actions
${xml_add_to_group_all_lamps}
var GroupAddress := 13;
modbusw(SH, 532, uint16, 0x60 + GroupAddress)
${xml_remove_from_group_all_lamps}
var GroupAddress := 13;
modbusw(SH, 532, uint16, 0x70 + GroupAddress)
Group 14 Dimmer Read and write

DALI broadcast group 14 (0–15) — write-only TapHome dimmer that broadcasts a single arc-power value to every gear in this group via H:520 GROUP LEVEL. The bus does not echo group levels, so TapHome holds the last written value.

UInt16 Unit: % numeric
Service Actions
Add All Lamps to GroupBroadcasts ADD TO GROUP via H:532 ALL COMMAND + REPEAT — every gear on the bus joins this group regardless of its current short address.
Remove All Lamps from GroupBroadcasts REMOVE FROM GROUP via H:532 ALL COMMAND + REPEAT — every gear on the bus leaves this group.

Group 14

Write level
var GroupAddress := 14;
modbusw(SH, 520, uint16, (GroupAddress << 8) + (Le * 0xFE));
Service Actions
${xml_add_to_group_all_lamps}
var GroupAddress := 14;
modbusw(SH, 532, uint16, 0x60 + GroupAddress)
${xml_remove_from_group_all_lamps}
var GroupAddress := 14;
modbusw(SH, 532, uint16, 0x70 + GroupAddress)
Group 15 Dimmer Read and write

DALI broadcast group 15 (0–15) — write-only TapHome dimmer that broadcasts a single arc-power value to every gear in this group via H:520 GROUP LEVEL. The bus does not echo group levels, so TapHome holds the last written value.

UInt16 Unit: % numeric
Service Actions
Add All Lamps to GroupBroadcasts ADD TO GROUP via H:532 ALL COMMAND + REPEAT — every gear on the bus joins this group regardless of its current short address.
Remove All Lamps from GroupBroadcasts REMOVE FROM GROUP via H:532 ALL COMMAND + REPEAT — every gear on the bus leaves this group.

Group 15

Write level
var GroupAddress := 15;
modbusw(SH, 520, uint16, (GroupAddress << 8) + (Le * 0xFE));
Service Actions
${xml_add_to_group_all_lamps}
var GroupAddress := 15;
modbusw(SH, 532, uint16, 0x60 + GroupAddress)
${xml_remove_from_group_all_lamps}
var GroupAddress := 15;
modbusw(SH, 532, uint16, 0x70 + GroupAddress)
Connection: Modbus RTU • 9600 baud• 8N1 • Slave ID: $[SlaveId]
Possible improvements (23)
  • H:531 ALL COMMAND (broadcast DALI command) — Single-shot broadcast DALI command (no repeat). Template uses H:532 (ALL COMMAND + REPEAT) for group membership but never exposes plain ALL OFF / ALL ON / ALL ARC POWER as a service action — turn-on/off-all are not present (sibling daliconfig template has them via H:530)
  • H:530 ALL LEVEL (broadcast arc power) — Sets brightness for every gear in one Modbus write. Convenient 'turn off all lamps' / 'turn on all lamps' shortcut — implemented in the sibling daliconfig template, missing in this operational template
  • H:521 GROUP COMMAND (per-group DALI command) — Per-group DALI command (single-shot). Could expose group-level OFF / RECALL MIN / RECALL MAX / GO TO SCENE as service actions on each Group device, but template only writes H:520 GROUP LEVEL
  • H:522 GROUP COMMAND + REPEAT — Per-group DALI configuration command (auto-repeated within 100 ms). Required for STORE DTR AS GROUP SCENE etc. — not exposed as a service action
  • H:541 DIRECT 16-BIT FRAME — Raw DALI 1.0 frame send. Used by sibling daliconfig template for the DALI 2.0 random-addressing algorithm; not needed for runtime brightness control
  • I:10020-I:10027 DALI 24-BIT FRAME — DALI 2.0 24-bit frames — required for DT8 colour control (XY, colour temperature, RGBW), DALI 2.0 control gear queries, and DALI-2 input devices (push-buttons, presence detectors). Template exposes only DALI 1.0 brightness layer per gear
  • I:10030-I:10047 DALI 25-BIT eDALI FRAME — Lunatone-proprietary 25-bit eDALI frames (auto and user redundant-bit variants). Not used by any DALI 1.0 gear
  • I:10050-I:10067 DALI 28-BIT / 32-BIT FRAME — Extended DALI 2.0 frames (memory bank access, DALI-2 application controllers). Template scope is single-channel arc power per gear
  • H:500 (DSI VALUE) DSI 8-bit value send — Send a raw 8-bit DSI/DALI value with no addressing (bus-level diagnostics). Bypassed by every TapHome script
  • C:0 / H:551 DALI Bus Error — 1 = DALI bus fault / short circuit / missing PSU. Useful diagnostic — could be added as a module-level service attribute. Currently bus errors only surface indirectly through 'Gear not responding!' errors emitted by per-gear readscripts
  • H:100 DALI PRIORITY SLOT — 0..4 → 12-16 ms forward-frame timing. Default 2 (configuration). Bus-tuning parameter; template runs at default
  • H:6000 DIP SWITCH / SOFTWARE RESET — Read returns the 4 DIP switch values; write 1 reboots the gateway. Sibling daliconfig template exposes Reset Gateway / Set Slave ID; this runtime template intentionally does not (commissioning concern)
  • H:65222 / H:65224 / H:65225 BAUD_RATE / PARITY / STOP_BITS (FLASH) — FLASH-stored serial parameters. Template hard-codes 9600 8N1 in connection_details, so these are out of scope
  • I:0 / I:50-I:53 / scenes DALI 1.0 scenes (recall / store / query) — DALI 1.0 standard supports 16 stored scenes per gear/group recallable via GO TO SCENE (0x10..0x1F) and storable via STORE DTR AS SCENE (0x40..0x4F). Template exposes per-gear ADD/REMOVE FROM GROUP and RESET, but no scene recall/store/remove/query actions
  • H:512 (RECALL MAX/MIN/GOTO LAST/UP/DOWN/STEP) DALI 1.0 high-level commands per gear — RECALL MAX LEVEL (0x05), RECALL MIN LEVEL (0x06), UP (0x01), DOWN (0x02), STEP UP (0x03), STEP DOWN (0x04), GO TO LAST ACTIVE LEVEL (0x0A) — useful as service actions for visual feedback / smooth dimming. Not exposed; brightness goes only through linear scaling
  • H:512 (STORE DTR AS MAX/MIN/POWER ON/SYSTEM FAILURE LEVEL) Per-gear configuration write commands — STORE DTR AS MAX LEVEL (0x2A), STORE DTR AS MIN LEVEL (0x2B), STORE DTR AS POWER ON LEVEL (0x2C), STORE DTR AS SYSTEM FAILURE LEVEL (0x2D), STORE DTR AS FADE TIME / FADE RATE — template has 'Set Startup Levels' (combines 0x21, 0x2C, 0x2D) but does NOT expose individual writes for max/min/fade-time/fade-rate
  • H:511 (QUERY FADE TIME / FADE RATE / RANDOM ADDRESS / EXT VERSION) Additional DALI queries — QUERY FADE TIME / FADE RATE (0xA5), QUERY VERSION NUMBER (0x97), QUERY RANDOM ADDRESS H/M/L (0x98/0xC2/0xC3), QUERY CONTENT DTR/DTR1/DTR2 — not exposed in service attributes, only the 10 attributes listed per gear are populated
  • H:511 (QUERY CONTROL GEAR / RESET STATE / LIMIT ERROR / LAMP POWER ON / MISSING SHORT ADDRESS) DALI gear health / topology queries — QUERY CONTROL GEAR (0x90), QUERY RESET STATE (0x95), QUERY LIMIT ERROR (0x94), QUERY LAMP POWER ON (0x93), QUERY MISSING SHORT ADDRESS (0x96, broadcast) — diagnostic queries. Could be useful as module-level diagnostics; template exposes only QUERY STATUS / LAMP FAILURE / POWER FAILURE / DEVICE TYPE / MIN-MAX-PHYSICAL MIN-POWER UP-SYSTEM FAILURE LEVEL / GROUPS
  • H:511 (READ MEMORY LOCATION 0xC5) DALI memory bank read — READ MEMORY LOCATION (0xC5) is required to read DALI 2.0 memory banks (Bank 0 device GTIN, FW version, serial number; Bank 1 for emergency lighting). Not used
  • ASCII protocol commands Parallel ASCII text protocol — RESI gateway also exposes a CR-terminated text protocol (#LARC, #GARC, #AARC, #LGS, #LCMD, #LCMDA, #DALI CMDxx etc.). Template uses Modbus exclusively — ASCII would only matter for diagnostics/terminal access
  • DT6 fade time / fade rate / DAPC sequence Smooth dimming primitives — DAPC (Direct Arc Power Control) sequence allows fast continuous level updates for smooth dimming. Each gear has stored fade time / fade rate that affect transitions. Template writes raw arc power without configuring fade behaviour
  • DT8 colour control (XY, colour temperature, RGBW) DALI DT8 tunable white / RGB / RGBW — DT8 colour control gears (tunable white CCT, XY colour, RGBW) require DALI 2.0 24-bit frames sent via H:541 / I:10020. Template exposes plain ModbusDimmer per gear — for DT8 lamps use a dedicated DT8 template
  • DT1 emergency lighting DALI DT1 self-contained emergency lights — Emergency-luminaire device type with periodic self-tests (function/duration test) and battery state queries. Out of scope — template is general-purpose dimming

Import parameters

Enter these values when you import the XML.

Asked in the appDefault
Slave ID255
IP address192.168.1.0

Sources

Manufacturer documents are © their respective owners. They are provided as published by the manufacturer, for reference only.

Found a problem with this device template?

Tell us what's wrong, what's missing, or how the template should behave. We rely on your feedback to keep the catalog accurate.

Template · TapHome portal Resi DALI Open this template in the Customer Portal to apply it to one of your homes, or to draft a refinement and submit it back to the catalog. Modbus RTU View in TapHome portal