Paradigm Control

Controlling the ETC Paradigm

The ETC Paradigm architectural control processor (LX-PARADIGM-ACP, 10.0.22.50) runs the house/architectural lighting — presets, macros, station lockout, relays. This page covers the ways to reach it, which of them work on our firmware, and — now confirmed onsite — the PSAP serial path with a full, validated object map.

   
Model Paradigm ACP in an ERn enclosure
Firmware 4.0.0.9.0.1027 (the saved project config is one build behind at …1026)
Web UI http://10.0.22.50/ — open as Administrator with no login
SSH Dropbear 2017.75 on port 22 (password + publickey). The built-in User/Administrator role passcodes don’t work here — see below.
Config Lighting/2018 Handover/Configuration Files/Sherwood Park Alliance Church, Rev 6.pcf, commissioned 2018-03-09 by FS.CA (Nelson Anselmo, job 3010083404)

The three control paths

Path Transport Status on our v4 processor
Web UI (Wt app) HTTP :80 Works. The only confirmed path today. See below.
v3 HTTP API (/get/*, /do/*) HTTP :80 Gone — returns 404. Only exists on firmware v3.
PSAP (Paradigm Serial Access Protocol) RS-232 (9600 8N1) Works — confirmed 2026-09-27. The only path that reaches macros/overrides. See below.

Web UI (the working path)

The web UI grants Administrator access with no passcode to anyone on the Lighting VLAN (the front-panel passcodes — User 2222, Admin 3333 — aren’t enforced on the web). This is worth tightening: the Setup page can set the default access level to Login.

It’s a Wt (C++ toolkit) single-page app. Two ways in:

  • &js=no fallback renders plain server-side HTML — curl-friendly. Bootstrap by fetching / for a session token (wtd=…), follow the redirect to ?_=/system, then navigate with the route parameter: ?_=/control, ?_=/network, ?_=/log, ?_=/setup&wtd=<token>&js=no.
  • Actions (activate a preset, etc.) are POSTs. Each button carries a per-session signal name (signal=sXXXX) that’s a server-side render-time counter — it changes every session, so it can’t be hard-coded. Fetch the page, read the signal off the target button’s own markup, and POST it once within that session. The field name is literally signal=sXXXX with an empty value.

Menu pages: System (status), Control (presets + sequences, with Activate/Deactivate/Record), Timed Events, Network (device roster), Setup (date/time + passcodes only), Log, Help. Serial/PSAP settings are not exposed anywhere in the web UI.

The Control page reads live preset state and can activate/deactivate/record. It does not list macros or overrides, so station lockout can’t be driven from here (see PSAP). The Log page records every change with parameters, e.g. Activate Preset (HTP) Works On/ Off [space=4, priority=100, fade=0.0, persist], and tags web-driven changes from the web UI — our own requests show up there.

v3 HTTP API (not on our firmware)

Older Paradigm firmware exposed an HTTP control API — GET /get/control_status, GET /get/<info>, GET /do/<action>?params. Those endpoints 404 on our processor: ETC replaced that API with the Wt app above at v4, and we’re on v4. There is no HTTP control path on our firmware — use PSAP.

SSH: tried, doesn’t take our passcodes

Dropbear 2017.75 answers on port 22 and offers password auth, so it’s tempting to reuse the two built-in roles. We tried — none of it works. The User and Administrator roles (passcodes 2222 / 3333, confirmed by the ACP manual: “the Admin passcode is ‘3333’ and the User passcode is ‘2222’”) are front-panel and web-UI access codes — four-digit login PINs for those roles — not OS-level shell accounts. Every combination was refused with Permission denied (publickey,password):

  • Usernames User, Administrator (and lowercase, Admin, admin, root, etc, paradigm)
  • Paired with passcodes 2222, 3333, and the project note’s default 1234

The Dropbear shell wants a real Unix user + password we don’t have (likely an ETC service account, not documented in our handover). So the role passcodes get you into the front panel and the web UI’s Login mode, but not the shell. This isn’t a control gap — PSAP already reaches everything we need — but it closes the “have we tried the logins over SSH?” question: yes, and they’re not SSH creds.

PSAP (the string protocol)

The project has ETC’s PSAP v4.0 script bound to the processor’s first Serial Input. PSAP is a plain-text command protocol — pst act <preset>[, <space>][, <fade>], macro on <name>, ovr enab <name>, plus read-only … get queries — over RS-232 or UDP (selectable per input). It’s the only path that can drive macros and overrides, including the Station Lock out macro that raises lockout level 50 on the five wall stations (macro on/off/get "Station Lock out").

Transport confirmed (2026-09-27): RS-232, 9600 8N1, CR-terminated. The processor answers on its first Serial Input over a plain straight-through cable — not a null-modem, despite the ACP manual; a null-modem in the chain produced dead silence, and removing it made it work. UDP drew no reply and is disabled in the config, so serial is the only serial-family path. The adapter is an FTDI (FT232R, serial B000PIWW) living on docker-server as /dev/ttyUSB0 (root:dialout).

Use the tool: Automation/server/scripts/paradigm-psap.py. Run from anywhere — if the serial port isn’t local it re-execs itself on docker-server inside a device-mapped container, so no dialout membership or sudo is needed:

paradigm-psap.py status                       # read every object's state (read-only)
paradigm-psap.py group "Auditorium" 50%       # set a zone intensity
paradigm-psap.py preset "House Out" on        # activate a preset (space supplied automatically)
paradigm-psap.py relay led on|off             # switch the LED / ML relay banks
paradigm-psap.py macro "Station Lock out" on  # raise station lockout
paradigm-psap.py raw "grp get Stairs"         # send a literal PSAP string
paradigm-psap.py                              # interactive menu of presets + macros (default)
paradigm-psap.py repl                         # command-style prompt (also reachable via `c` in the menu)

The tool knows each object’s type, space and verb, so you refer to things by name and it builds the right command — presets get their , <space> suffix, groups take 0-255/N%, macros take on/off. Every mutating command reads the state back and prints a ✓/✗ confirmation (e.g. Auditorium -> 128 ✓); a mismatch is loud. Run with no subcommand for an interactive menu that lists every preset (by space) and macro with live state and toggles them by number. The repl mode drives objects by name the same way — Preset 2 on, Auditorium 50%, HLX Row 3 128, a bare name to query — and still accepts a literal PSAP string. The confirmation is processor state, not proof of a physical change.

Message framing: commands are terminated by the End of Message Char, configured here as a carriage return (\r). Log Level is set to Errors — invalid commands draw an error reply, which makes a bare invalid string (e.g. zzz\r) the safe first test that a connection is live. Objects are addressed by name (there’s no “list all” command); levels are 0-255 or N%; optional trailing args are [, spacename][, fadetime].

Family Commands
Channel chan int:LVL name · ras: · low: · tog name · min:LVL · max:LVL · get name
Group grp int:LVL name · ras: · low: · tog name · get name
Preset (LTP) pst act[:pri] name · dact name · tog[:pri] name · rec name · get name
Preset (HTP) pst acth[:pri] name · dacth name · togh[:pri] name · geth name
Sequence seq start[:pri] name · stop · pause · resume · rate:LVL · get name
Space spc off name · ras:LVL · low:LVL · master:LVL name
Wall wall open name · close · tog · get name
Macro macro on name · off · tog · cancel · get name
Override ovr enab name · (disable/status per script)
Contact con set:N name · slr:N name

Reply grammar (a read-only oracle). A valid … get echoes state in command form (grp int:255 Stairs, Global, pst dact Preset 1, House, macro off Station Lock out, ovr disab Relay Power Overide); an invalid command returns error invalid <type> "<name>". So any bogus string is a safe liveness probe, and get is a safe way to poll state without changing it. Sets are silent (empty reply) and take effect essentially instantly — the Work Light group snapped 0↔255 with no fade, and the Works On/ Off preset turned on within a query round-trip and dropped ~0.25 s slower than it made (a relay release lag). Confirm a set with a follow-up get, never by waiting for an ack.

Firmware gotcha — every preset command needs its space. This processor runs v4.0.0, which has a documented ETC bug that requires the space suffix on preset strings: pst act Preset 1 errors invalid preset, but pst act Preset 1, House works. (Fixed in v4.1.0; we’re not there.) Groups, macros and overrides don’t need it. The tool always appends the space from the map below.

Validated object map (queried live 2026-09-27)

Object names are not the touchscreen labels — they come from the config’s own tables (groups.elp, macros.elp, overrides.elp, spaces.elp, scenes/*.elp, stored as Qt UTF-16 QStrings) and were then confirmed over the wire. There is no “list all” command, so this table is the inventory.

Presets — pst act <name>, <space> / pst dact <name>, <space>:

Space Presets
House Preset 1–Preset 9, House Out
Universe 512 Stage 1–Stage 8
Works Works On/ Off
Performance Circuits LED Relays On, LED Relays Off, ML Relays On, ML Relays Off, All Relays & Dimmers Off

Groups — grp int:<0-255> <name> (all in space Global): MASTER, Auditorium, Stairs, Wing Pots, Wing Doors, HLX Row1, HLX Row 2, HLX Row 3, HLX Row 4, HLX All, Organ Pipes, Work Light. Groups are the direct way to drive a patched zone.

Caution — not every object is patched to output. The processor accepts and echoes commands to objects that drive nothing physical, so a successful reply is not proof of a room change. Confirmed 2026-09-27: setting the Work Light group echoes levels (grp int:255 Work Light) but changes nothing in the room. The actual stage work lights are the Works On/ Off preset (a relay in space Works) — pst act Works On/ Off, Works turns them on. The tool’s worklights on|off drives that preset. Verify physical changes by eye, not by the reply.

Macros — macro on|off|get <name>: Station Lock out, Power Lock out, Stage Record Lockout, CC1. Station Lock out raises lockout level 50 on the five wall stations; PSAP itself isn’t a station, so lockout never blocks PSAP.

Overrides — ovr enab|disab|get <name>: Station locked out, Relay Power Overide.

There are no ETC “Presets” in the classic sense and no walls configured; the station scenes live in the scenes/ tables and are reached as the space-qualified presets above.

Serial pinout (from the ACP manual)

RS-232 on a male DB9. Pin 2 = Rx, 3 = Tx, 5 = ground, 9600 8N1. In practice our FTDI adapter reaches the processor over a straight-through cable — the manual’s null-modem note did not apply to our chain (a null-modem in line gave dead silence; removing it worked). See Lighting/2018 Handover/User Manuals/6. House Light Controls/6.2 Paradigm_ACP_ConfigManual_v.4_revA.pdf.

What we haven’t done

  • Logged into the web UI’s Login mode (a project note mentions a default password 1234, untried there). SSH itself has been tried — see below.
  • Exercised the Contact family (con set:N name) — the likely path behind the LED/ML relay presets — or the Sequence family; neither is needed for normal preset/group/macro control.