Compare commits

...

2 commits

Author SHA1 Message Date
cae1ac416e Add deprecation notice pointing to lutron-control 2026-06-28 08:34:32 -05:00
b9fa97faa9 Rewrite README for beginners and auto-elevate install.sh
Make the README approachable for non-experts with a jargon glossary and
step-by-step framing. Have install.sh re-exec itself under sudo and infer
the service user from SUDO_USER, prompting when the 'pi' fallback is absent.
2026-06-03 20:41:51 -05:00
2 changed files with 221 additions and 125 deletions

312
README.md
View file

@ -1,53 +1,97 @@
This project controls the GRAFIK Eye QS control panel via a QSE-CI-NWK-E over its
serial interface. It uses the [OLA](https://www.openlighting.org/) project to take
a DMX device or a network DMX protocol (e.g. sACN/E1.31) and drive the 6 available
zones. It also speaks MQTT for Home Assistant control, with MQTT auto discovery so
the light appears automatically.
> [!WARNING]
> **This project is deprecated.** It has been superseded by
> [grmrgecko/lutron-control](https://github.com/grmrgecko/lutron-control).
> Please use that project instead.
I run this on a Raspberry Pi (a Pi Zero works) on **Raspberry Pi OS / Raspbian 13
(Trixie)**. OLA is no longer packaged for recent Debian/Raspbian releases, so it is
built from source at the `0.10.9` release tag. Just run the included `install-ola.sh`
(see below) — it handles the whole build for you. The script was written based on the
[OLA build guide](https://www.openlighting.org/ola/linuxinstall/), so you don't need
to follow that guide yourself; it's linked only as a reference for what the script does.
# Lutron GRAFIK Eye QS — DMX & Home Assistant control
DMX and MQTT are independent, optional components. Serial control of the QSE is
always active; you can run with DMX only, MQTT only, or both.
This lets you control a **Lutron GRAFIK Eye QS** lighting unit from two places it
normally can't be reached from:
- **A lighting board / theatrical software** (so the GRAFIK Eye's zones can be run
as part of a larger light show), and
- **Home Assistant** (so you can control and automate the lights like any other
smart light in your house).
It runs as a small always-on program on a **Raspberry Pi** that you wire to the
GRAFIK Eye. The Pi listens for commands and translates them into the GRAFIK Eye's
own language. The two control methods are independent and both optional — use one,
the other, or both.
> **New to the jargon?** Here's the short version:
> - **GRAFIK Eye QS** — the Lutron lighting control unit this drives. It has up to
> 6 dimmable lighting *zones*.
> - **QSE-CI-NWK-E** — the add-on module on the GRAFIK Eye that gives it a serial
> port we can talk to.
> - **DMX / sACN (E1.31)** — the standard "language" lighting boards and stage
> software use. sACN is just DMX sent over your normal network instead of a cable.
> - **OLA (Open Lighting Architecture)** — free software that receives the network
> DMX and hands it to this program. Only needed if you want lighting-board control.
> - **MQTT** — the messaging system Home Assistant uses to talk to devices. Only
> needed if you want Home Assistant control.
## How it's set up here
I run this on a Raspberry Pi (a Pi Zero is plenty) on **Raspberry Pi OS / Raspbian
13 (Trixie)**. Talking to the GRAFIK Eye over serial is always on. DMX and MQTT are
separate optional pieces you can turn on or off in the config file.
One catch with DMX: OLA is no longer pre-packaged for recent Raspberry Pi OS
releases, so it has to be *built from source* (compiled on the Pi). Don't worry —
the included `install-ola.sh` script does the entire build for you. It follows the
official [OLA build guide](https://www.openlighting.org/ola/linuxinstall/), so you
don't have to; that link is there only if you're curious what the script is doing.
# What you'll need
- A **Raspberry Pi** running Raspberry Pi OS (a Pi Zero is enough; a Pi Zero **W**
or any model with networking is needed for sACN/MQTT). These instructions assume
**Raspberry Pi OS / Raspbian 13 (Trixie)**.
- A **USB-to-serial adapter** wired to the QSE-CI-NWK-E's serial terminals (the
config example uses a Prolific PL2303-style adapter; any 3.3 V / RS-232 adapter
that matches your wiring works).
- A **GRAFIK Eye QS** with a QSE-CI-NWK-E network/serial interface.
- For DMX: a lighting console or software sending **sACN/E1.31** on your network.
- For MQTT / Home Assistant: a running **MQTT broker** (the Docker setup below
includes one).
- A **Raspberry Pi** running Raspberry Pi OS. A Pi Zero is enough for serial-only
control; for sACN or Home Assistant you need networking, so use a Pi Zero **W**
(Wi-Fi) or any networked model. These instructions assume **Raspberry Pi OS /
Raspbian 13 (Trixie)**.
- A **USB-to-serial adapter** wired to the QSE-CI-NWK-E's serial terminals. The
example config uses a common Prolific PL2303-style adapter; any 3.3 V / RS-232
adapter that matches your wiring will do.
- A **GRAFIK Eye QS** with the **QSE-CI-NWK-E** network/serial interface module.
- **For lighting-board control:** a lighting console or software that sends
**sACN/E1.31** over your network.
- **For Home Assistant:** a running **MQTT broker** (the Docker setup near the end
of this guide includes one).
# Overview
# The big picture
The setup is three steps once the Pi is ready:
Once your Pi is up and running, setup is just a few steps:
1. **Prepare the Pi and get the code** (step 0) — flash the OS, get a terminal, clone this repo.
2. **Install OLA** (step 1) — only if you use DMX. This is the slow part (~12 h on a Pi Zero).
3. **Install the control service** (step 2) and **configure it** (step 3).
1. **Get the Pi ready and download the code** (step 0).
2. **Install OLA** (step 1) — *only if you want lighting-board/DMX control.* This is
the slow part (~12 hours on a Pi Zero, because it compiles from source).
3. **Install the control program** (step 2) and **fill in the config** (step 3).
Everything below is typed into a terminal on the Pi. If you've never used one, the
commands are copy-paste — just swap in your own values where noted.
# Installation
## 0. Prepare the Pi and get the code
## 0. Get the Pi ready and download the code
If you're starting from scratch, flash **Raspberry Pi OS** with the
[Raspberry Pi Imager](https://www.raspberrypi.com/software/). In the imager's
settings (the gear / "Edit settings"), **set a username and password and enable
SSH** — remember the username you choose; you'll use it everywhere below as
`<user>`. Modern Raspberry Pi OS no longer defaults to the `pi` user, so don't
assume it; use whatever name you set here.
If you're starting from a blank SD card, flash **Raspberry Pi OS** with the
[Raspberry Pi Imager](https://www.raspberrypi.com/software/). Before you write the
card, open the imager's settings (the **gear** / **"Edit settings"** button) and:
Boot the Pi, then open a terminal on it (directly, or over SSH:
`ssh <user>@<pi-address>`). Install git and download this project:
- **Set a username and password**, and
- **Enable SSH** (so you can connect to the Pi from another computer).
**Write down the username you choose** — you'll use it all over this guide, shown as
`<user>`. Modern Raspberry Pi OS no longer uses `pi` as the default username, so use
whatever you set here.
Boot the Pi, then open a terminal on it — either directly with a keyboard and
monitor, or from another computer over SSH:
```bash
ssh <user>@<pi-address>
```
Now install git and download this project:
```bash
sudo apt-get update
@ -56,120 +100,143 @@ git clone https://github.com/GRMrGecko/lutron-dmx-control.git
cd lutron-dmx-control
```
All the commands below are run from inside this `lutron-dmx-control` directory.
Every command from here on is run from inside this `lutron-dmx-control` folder.
> Throughout this guide, replace `<user>` with the username you created above. If
> that username is **not** `pi`, you must also pass it to the installer
> (`TARGET_USER=<user>`, shown in step 2) and substitute it in every
> `systemctl`/`journalctl` command (e.g. `lutron-dmx-control@<user>`, not `@pi`).
> Throughout this guide, replace `<user>` with the username you created above. For
> example, `lutron-dmx-control@<user>` becomes `lutron-dmx-control@john` if your
> username is `john`.
## 1. Install OLA (only if using DMX)
## 1. Install OLA (only if you want DMX / lighting-board control)
If you set `dmx.enabled: false`, skip this step — OLA does not need to be installed.
**Not using a lighting board?** Skip this step entirely. Set `dmx.enabled: false` in
the config (step 3) and OLA never has to be installed.
Otherwise build and install OLA (the daemon plus the Python client bindings the
control script uses). On a single-core Pi Zero this takes roughly 12 hours; the
script adds temporary swap on low-memory boards so the compile does not run out of
memory.
Otherwise, build and install OLA (the background service plus the Python add-on this
program uses):
```bash
bash ./install-ola.sh
```
This installs the build dependencies, clones OLA at the `0.10.9` tag, and builds and
installs `olad` plus the `ola.ClientWrapper` Python module. Override the version or
build directory with `OLA_VERSION=` / `BUILD_DIR=` if needed.
This installs the build tools, downloads OLA at the tested `0.10.9` version, and
compiles and installs it. On a single-core Pi Zero the compile takes roughly **12
hours** — that's normal. The script temporarily adds extra memory (swap) on
low-memory boards so the build doesn't run out of memory partway through.
## 2. Install the control service
If you ever need to, you can override the version or build location with the
`OLA_VERSION=` / `BUILD_DIR=` environment variables.
`install.sh` installs the Python dependencies, the control script, the config file
and the `olad@<user>` / `lutron-dmx-control@<user>` systemd services. By default it
installs for the `pi` user; pass `TARGET_USER=<name>` for a different user.
## 2. Install the control program
Run the installer:
```bash
sudo bash ./install.sh
# or, for a non-pi user:
sudo TARGET_USER=james bash ./install.sh
./install.sh
```
The service is **enabled** (starts on boot) but, on a first install, is **not
started immediately** — the freshly installed config still has placeholder values.
The installer prints the exact edit-then-start steps; see step 3 below. On a re-run
with an existing config it restarts the service to pick up the new version.
It installs the Python requirements, the control program itself, the config file,
and the background services that keep everything running and start it on boot.
> Note: the systemd unit runs `/home/<user>/lutron-dmx-control.py`, so `<user>`'s
> home must be `/home/<user>`. If it lives elsewhere, the installer warns you to
> adjust `ExecStart` in `lutron-dmx-control@.service`.
On a **first install** the service is set to start on boot but is **not started
yet** — the config still has placeholder values you need to fill in. The installer
prints the exact "edit, then start" steps for you (covered in step 3). If you run
`install.sh` again later (to update), it restarts the service to pick up the new
version.
## 3. Configure
## 3. Fill in the config
Edit `/etc/lutron-dmx-control/config.yaml` (installed from `config.example.yaml`) and set:
Open the config file in a text editor (`nano` is beginner-friendly):
- `serial.device` — your serial device (use `ls -lah /dev/serial/by-id/`).
- `qse.integration_id` and `qse.zones` — to match your GRAFIK Eye unit.
- `dmx.enabled` / `dmx.universe` / `dmx.start_address` — for your DMX layout.
`dmx.lockout_sec` (default `5`) sets how long an active DMX signal locks out MQTT
control. Set `dmx.enabled: false` to run without OLA/DMX.
- `mqtt.broker`, `mqtt.username`, `mqtt.password` — if using MQTT. Set
`mqtt.enabled: false` to run without MQTT/Home Assistant; `paho-mqtt` is then not
required.
```bash
sudo nano /etc/lutron-dmx-control/config.yaml
```
The config is searched for at `--config PATH`, then `$LUTRON_CONFIG`, then `config.yaml`
next to the script, then `~/.config/lutron-dmx-control/config.yaml`, then
`/etc/lutron-dmx-control/config.yaml`. It holds the MQTT password, so it is `chmod 600`
and excluded from git (`config.yaml` in `.gitignore`); only `config.example.yaml` is
committed.
This file was created from `config.example.yaml` and is heavily commented, so each
setting explains itself. The important ones:
Then start (first install) or restart (after edits) the service:
`sudo systemctl start lutron-dmx-control@pi` (use `restart` if it is already running).
Check it came up cleanly with `journalctl -u lutron-dmx-control@pi -f`.
- **`serial.device`** — which USB-serial adapter to use. Find yours by running
`ls -lah /dev/serial/by-id/` and copying the matching path.
- **`qse.integration_id` and `qse.zones`** — set these to match your GRAFIK Eye
(the integration ID is assigned in Lutron's programming; zones is how many
dimmable zones your model has).
- **`dmx.*`** — your DMX layout (`universe`, `start_address`). `dmx.lockout_sec`
(default `5`) is how long an active DMX signal keeps Home Assistant from changing
the lights, so the lighting board stays in charge during a show. Set
`dmx.enabled: false` to run without DMX/OLA.
- **`mqtt.*`** — your MQTT broker address and `username`/`password` for Home
Assistant. Set `mqtt.enabled: false` to run without MQTT (then `paho-mqtt` isn't
needed).
Save and exit (`Ctrl+O`, `Enter`, then `Ctrl+X` in nano).
> **Where the config lives:** the program looks for it in this order — `--config
> PATH`, then `$LUTRON_CONFIG`, then a `config.yaml` next to the program, then
> `~/.config/lutron-dmx-control/config.yaml`, then
> `/etc/lutron-dmx-control/config.yaml` (where the installer puts it). Because it
> holds your MQTT password, it's locked down (`chmod 600`) and kept out of git; only
> the `config.example.yaml` template is committed.
Now start the service (use `restart` instead of `start` if it's already running, e.g.
after editing the config). **Remember to replace `<user>` with your username:**
```bash
sudo systemctl start lutron-dmx-control@<user>
```
Check that it started cleanly (press `Ctrl+C` to stop watching the log):
```bash
journalctl -u lutron-dmx-control@<user> -f
```
# OLA / DMX configuration
`install.sh` configures OLA for **network DMX only (E1.31/sACN)** by default: it
disables every OLA plugin except `e131`. This matters because olad's serial/USB
device plugins (e.g. `usbserial`) otherwise auto-probe and grab the QSE's serial
adapter (`/dev/ttyUSB*`), conflicting with this program. The plugin configs live in
`~/.ola/` if you want to change this later.
By default, `install.sh` sets OLA up for **network DMX only (E1.31/sACN)**: it turns
off every OLA plugin except `e131`. This matters because OLA's serial/USB plugins
would otherwise grab your USB-serial adapter (`/dev/ttyUSB*`) out from under this
program. The plugin settings live in `~/.ola/` if you want to change them later.
To enable a different/extra plugin, stop olad, flip its config, and restart:
To turn on a different or extra plugin, stop OLA, change its setting, and start it
again (replace `<user>` with your username):
```bash
sudo systemctl stop olad@pi
sudo systemctl stop olad@<user>
sed -i '/^enabled\s*=/c\enabled = true' ~/.ola/ola-artnet.conf # example: also accept Art-Net
sudo systemctl start olad@pi
sudo systemctl start olad@<user>
```
## Receiving sACN (patching the universe)
For olad to actually receive sACN, an **E1.31 input port must be patched to your OLA
universe** — the OLA universe number is the sACN universe (e.g. universe `3` =
multicast `239.255.0.3`). Registering the universe from the client is not enough;
without a patched input port olad never joins the sACN multicast group.
For OLA to actually *receive* sACN, an **E1.31 input port has to be "patched" to your
OLA universe**. The OLA universe number is the same as the sACN universe (e.g.
universe `3` = multicast address `239.255.0.3`). Just registering the universe isn't
enough — without a patched input port, OLA never joins the network group that carries
the DMX data.
`install.sh` does this automatically: it patches the E1.31 input port to the
`install.sh` does this for you automatically, patching the E1.31 input port to the
`dmx.universe` from your `config.yaml`. To do it (or change it) by hand:
```bash
# Find the E1.31 device id, then patch input port 0 to your universe (here 3):
ola_dev_info
ola_patch --device 1 --port 0 --input --universe 3
# Confirm the multicast join on your active interface (eth0 wired, wlan0 on a Pi Zero W):
# Confirm it joined the multicast group on your network interface
# (eth0 = wired, wlan0 = Wi-Fi on a Pi Zero W):
ip maddr show dev eth0 | grep 239.255.0.3
curl -s http://localhost:9090/get_dmx?u=3 # confirm DMX values are arriving
```
You can also patch from the olad web UI at the Pi's IP, port `9090`. The patch is
saved in `~/.ola/` and survives restarts/reboots.
You can also do this from OLA's web page — browse to the Pi's IP address on port
`9090`. The patch is saved in `~/.ola/` and survives restarts and reboots.
> Note: on the console/desktop sending sACN, a "changes only" / "send on change"
> option means it only transmits when levels change. Prefer a continuous stream so
> olad has data immediately after a restart.
> **Tip:** on the console or software sending sACN, a "changes only" / "send on
> change" option means it only transmits when levels change. Prefer a continuous
> stream so OLA has data to work with immediately after a restart.
# Home Assistant & MQTT (Docker)
I run Home Assistant and the Mosquitto MQTT broker in Docker via `docker compose`.
A minimal `compose.yaml`:
I run Home Assistant and the Mosquitto MQTT broker in Docker using `docker compose`.
Here's a minimal `compose.yaml`:
```yaml
services:
@ -191,12 +258,14 @@ services:
network_mode: host
```
`network_mode: host` lets Home Assistant discover the broker and the control script
publish to it on `127.0.0.1:1883`. Bring it up with `docker compose up -d`.
`network_mode: host` lets Home Assistant find the broker and lets this control
program publish to it at `127.0.0.1:1883`. Start it all with `docker compose up -d`.
## Mosquitto config
Mosquitto needs a config and a password in the mounted `./mosquitto` directory.
Mosquitto (the MQTT broker) needs a config file and a password in the mounted
`./mosquitto` folder.
`./mosquitto/mosquitto.conf`:
```
@ -208,15 +277,15 @@ password_file /mosquitto/config/pwfile
acl_file /mosquitto/config/aclfile
```
`./mosquitto/aclfile` (grant the `mqtt` user full access):
`./mosquitto/aclfile` (gives the `mqtt` user full access):
```
user mqtt
topic readwrite #
```
Create the password file (use the same `mqtt` user/password you put in
`config.yaml`):
Create the password file — use the **same `mqtt` user and password you put in
`config.yaml`**:
```bash
docker compose run --rm mqtt mosquitto_passwd -c -b /mosquitto/config/pwfile mqtt 'your-password'
@ -225,12 +294,14 @@ docker compose restart mqtt
## Home Assistant integration
In Home Assistant, add the **MQTT** integration (Settings → Devices & Services) and
point it at the broker (host `127.0.0.1`, port `1883`, the `mqtt` user/password).
In Home Assistant, add the **MQTT** integration (**Settings → Devices & Services**)
and point it at the broker: host `127.0.0.1`, port `1883`, and the `mqtt`
user/password.
With `mqtt.discovery: true` (the default in `config.yaml`), the light is published via
Home Assistant MQTT discovery and appears automatically — no YAML needed. To disable
discovery, set `mqtt.discovery: false` and add the light manually:
With `mqtt.discovery: true` (the default in `config.yaml`), the light is announced to
Home Assistant automatically and shows up on its own — no YAML editing required. If
you'd rather add it manually, set `mqtt.discovery: false` and add this to your Home
Assistant config:
```yaml
light:
@ -244,18 +315,19 @@ light:
supported_color_modes: ["brightness"]
```
# Recommended: watchdog
# Recommended: hardware watchdog
Enable the hardware watchdog on the Pi to auto-reboot on a system crash.
The Raspberry Pi has a built-in hardware watchdog that can automatically reboot the
Pi if it ever locks up. It's worth enabling for an always-on device like this.
Add to `/boot/firmware/config.txt` (or `/boot/config.txt` on older images) under the
`[all]` section:
Add this to `/boot/firmware/config.txt` (or `/boot/config.txt` on older images) under
the `[all]` section:
```
watchdog=on
```
Uncomment `RuntimeWatchdogSec` in `/etc/systemd/system.conf` and set it:
Then uncomment `RuntimeWatchdogSec` in `/etc/systemd/system.conf` and set it:
```
RuntimeWatchdogSec=10s

View file

@ -2,17 +2,41 @@
set -e
USER=$(whoami)
if [ "$USER" != "root" ]; then
echo "Please use sudo with this install script to ensure right permissions for installation."
# Determine the service user before we (potentially) re-exec as root, so we can
# capture the human who invoked the script. Priority: explicit TARGET_USER env,
# then $SUDO_USER (set when already run via sudo), then the current user. We fall
# back to "pi" only if we somehow end up with an empty or root value.
DEFAULTED_TO_PI=0
if [ -z "$TARGET_USER" ]; then
TARGET_USER="${SUDO_USER:-$(whoami)}"
fi
if [ -z "$TARGET_USER" ] || [ "$TARGET_USER" = "root" ]; then
TARGET_USER="pi"
DEFAULTED_TO_PI=1
fi
# Re-run ourselves under sudo if we are not already root. Pass TARGET_USER through
# so the resolved (non-root) user survives the privilege escalation.
if [ "$(id -u)" -ne 0 ]; then
if command -v sudo >/dev/null 2>&1; then
exec sudo TARGET_USER="$TARGET_USER" "$0" "$@"
fi
echo "Please run this install script as root (or install sudo) to ensure right permissions for installation."
exit 1
fi
# Service user (matches the systemd template instance: lutron-dmx-control@<TARGET_USER>).
TARGET_USER="${TARGET_USER:-pi}"
TARGET_HOME=$(getent passwd "$TARGET_USER" | cut -d: -f6)
# If we couldn't infer a user and the "pi" fallback doesn't exist on this system,
# ask which user the service should run as rather than failing outright.
if [ -z "$TARGET_HOME" ] && [ "$DEFAULTED_TO_PI" -eq 1 ]; then
read -r -p "Default user 'pi' not found. Which user should the service run as? " TARGET_USER
TARGET_HOME=$(getent passwd "$TARGET_USER" | cut -d: -f6)
fi
if [ -z "$TARGET_HOME" ]; then
echo "Target user '$TARGET_USER' does not exist. Re-run with TARGET_USER=<name> sudo ./install.sh"
echo "Target user '$TARGET_USER' does not exist. Re-run with TARGET_USER=<name> ./install.sh"
exit 1
fi