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=nofallback 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 literallysignal=sXXXXwith 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 default1234
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 Lightgroup echoes levels (grp int:255 Work Light) but changes nothing in the room. The actual stage work lights are theWorks On/ Offpreset (a relay in spaceWorks) —pst act Works On/ Off, Worksturns them on. The tool’sworklights on|offdrives 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
Contactfamily (con set:N name) — the likely path behind the LED/ML relay presets — or theSequencefamily; neither is needed for normal preset/group/macro control.