Improve setup docs and finalize DMX/MQTT install tooling
- README: add 'What you'll need', overview, and a 'Prepare the Pi and get the code' step (git clone) so a non-expert can set up from a blank Pi; flag the non-'pi' default username gotcha. - Add config.example.yaml and install-ola.sh; update install.sh and the systemd unit for the env-file/OLA-from-source workflow. - Remove the local Home Assistant/Mosquitto test files from the repo and ignore config.yaml (holds the MQTT password).
This commit is contained in:
parent
979c865262
commit
21fe3a1476
26 changed files with 1303 additions and 561 deletions
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Local config holds secrets (MQTT password); never commit it.
|
||||
config.yaml
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
version: '3'
|
||||
services:
|
||||
homeassistant:
|
||||
container_name: home-assistant
|
||||
image: homeassistant/home-assistant:stable
|
||||
volumes:
|
||||
- ./hass:/config
|
||||
environment:
|
||||
- TZ=America/Chicago
|
||||
restart: always
|
||||
devices:
|
||||
- /dev/ttyUSB1:/dev/ttyUSB1
|
||||
- /dev/serial/by-id/usb-Silicon_Labs_HubZ_Smart_Home_Controller_415007C7-if01-port0
|
||||
network_mode: host
|
||||
mqtt:
|
||||
image: eclipse-mosquitto
|
||||
volumes:
|
||||
- ./mosquitto:/mosquitto/config
|
||||
restart: always
|
||||
network_mode: host
|
||||
zwave-js:
|
||||
container_name: zwavejs2mqtt
|
||||
image: zwavejs/zwavejs2mqtt:latest
|
||||
volumes:
|
||||
- ./zwave-js:/usr/src/app/store
|
||||
devices:
|
||||
- /dev/ttyUSB0:/dev/ttyUSB0
|
||||
- /dev/serial/by-id/usb-Silicon_Labs_HubZ_Smart_Home_Controller_415007C7-if00-port0
|
||||
environment:
|
||||
- TZ=America/Chicago
|
||||
restart: always
|
||||
network_mode: host
|
||||
|
|
@ -1 +0,0 @@
|
|||
2021.1.5
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
{
|
||||
"version": 1,
|
||||
"key": "core.entity_registry",
|
||||
"data": {
|
||||
"entities": [
|
||||
{
|
||||
"entity_id": "binary_sensor.updater",
|
||||
"config_entry_id": null,
|
||||
"device_id": null,
|
||||
"area_id": null,
|
||||
"unique_id": "updater",
|
||||
"platform": "updater",
|
||||
"name": null,
|
||||
"icon": null,
|
||||
"disabled_by": null,
|
||||
"capabilities": null,
|
||||
"supported_features": 0,
|
||||
"device_class": null,
|
||||
"unit_of_measurement": null,
|
||||
"original_name": "Updater",
|
||||
"original_icon": null
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"version": 1,
|
||||
"key": "core.uuid",
|
||||
"data": {
|
||||
"uuid": "6ae2a5eefe6741829c95a45064c93a0f"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
{
|
||||
"version": 1,
|
||||
"key": "http",
|
||||
"data": {
|
||||
"login_attempts_threshold": -1,
|
||||
"server_port": 8123,
|
||||
"cors_allowed_origins": [
|
||||
"https://cast.home-assistant.io"
|
||||
],
|
||||
"ip_ban_enabled": true,
|
||||
"ssl_profile": "modern"
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
[]
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
blueprint:
|
||||
name: Motion-activated Light
|
||||
description: Turn on a light when motion is detected.
|
||||
domain: automation
|
||||
source_url: https://github.com/home-assistant/core/blob/dev/homeassistant/components/automation/blueprints/motion_light.yaml
|
||||
input:
|
||||
motion_entity:
|
||||
name: Motion Sensor
|
||||
selector:
|
||||
entity:
|
||||
domain: binary_sensor
|
||||
device_class: motion
|
||||
light_target:
|
||||
name: Light
|
||||
selector:
|
||||
target:
|
||||
entity:
|
||||
domain: light
|
||||
no_motion_wait:
|
||||
name: Wait time
|
||||
description: Time to leave the light on after last motion is detected.
|
||||
default: 120
|
||||
selector:
|
||||
number:
|
||||
min: 0
|
||||
max: 3600
|
||||
unit_of_measurement: seconds
|
||||
|
||||
# If motion is detected within the delay,
|
||||
# we restart the script.
|
||||
mode: restart
|
||||
max_exceeded: silent
|
||||
|
||||
trigger:
|
||||
platform: state
|
||||
entity_id: !input motion_entity
|
||||
from: "off"
|
||||
to: "on"
|
||||
|
||||
action:
|
||||
- service: light.turn_on
|
||||
target: !input light_target
|
||||
- wait_for_trigger:
|
||||
platform: state
|
||||
entity_id: !input motion_entity
|
||||
from: "on"
|
||||
to: "off"
|
||||
- delay: !input no_motion_wait
|
||||
- service: light.turn_off
|
||||
target: !input light_target
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
blueprint:
|
||||
name: Zone Notification
|
||||
description: Send a notification to a device when a person leaves a specific zone.
|
||||
domain: automation
|
||||
source_url: https://github.com/home-assistant/core/blob/dev/homeassistant/components/automation/blueprints/notify_leaving_zone.yaml
|
||||
input:
|
||||
person_entity:
|
||||
name: Person
|
||||
selector:
|
||||
entity:
|
||||
domain: person
|
||||
zone_entity:
|
||||
name: Zone
|
||||
selector:
|
||||
entity:
|
||||
domain: zone
|
||||
notify_device:
|
||||
name: Device to notify
|
||||
description: Device needs to run the official Home Assistant app to receive notifications.
|
||||
selector:
|
||||
device:
|
||||
integration: mobile_app
|
||||
|
||||
trigger:
|
||||
platform: state
|
||||
entity_id: !input person_entity
|
||||
|
||||
variables:
|
||||
zone_entity: !input zone_entity
|
||||
# This is the state of the person when it's in this zone.
|
||||
zone_state: "{{ states[zone_entity].name }}"
|
||||
person_entity: !input person_entity
|
||||
person_name: "{{ states[person_entity].name }}"
|
||||
|
||||
condition:
|
||||
condition: template
|
||||
value_template: "{{ trigger.from_state.state == zone_state and trigger.to_state.state != zone_state }}"
|
||||
|
||||
action:
|
||||
domain: mobile_app
|
||||
type: notify
|
||||
device_id: !input notify_device
|
||||
message: "{{ person_name }} has left {{ zone_state }}"
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
# Configure a default setup of Home Assistant (frontend, api, etc)
|
||||
default_config:
|
||||
|
||||
homeassistant:
|
||||
name: Church
|
||||
latitude: 34.719930
|
||||
longitude: -86.704050
|
||||
elevation: 470
|
||||
unit_system: imperial
|
||||
time_zone: "America/Chicago"
|
||||
legacy_templates: false
|
||||
|
||||
logger:
|
||||
default: info
|
||||
|
||||
# Text to speech
|
||||
tts:
|
||||
- platform: google_translate
|
||||
|
||||
group: !include groups.yaml
|
||||
automation: !include automations.yaml
|
||||
script: !include scripts.yaml
|
||||
|
||||
zha:
|
||||
database_path: /config/zigbee.db
|
||||
enable_quirks: true
|
||||
|
||||
mqtt:
|
||||
discovery: true
|
||||
broker: 127.0.0.1
|
||||
port: 1883
|
||||
username: !secret mqtt_username
|
||||
password: !secret mqtt_password
|
||||
birth_message:
|
||||
topic: 'homeassistant/status'
|
||||
payload: 'online'
|
||||
will_message:
|
||||
topic: 'homeassistant/status'
|
||||
payload: 'offline'
|
||||
|
||||
light:
|
||||
- platform: mqtt
|
||||
schema: json
|
||||
name: lutron_qse_nwk
|
||||
state_topic: "lutron/qse-nwk"
|
||||
command_topic: "lutron/qse-nwk/set"
|
||||
brightness: true
|
||||
color_mode: true
|
||||
supported_color_modes: ["brightness"]
|
||||
Binary file not shown.
|
|
@ -1,2 +0,0 @@
|
|||
2021-09-02 23:56:55 WARNING (MainThread) [homeassistant.components.http.ban] Login attempt or request with invalid authentication from 10.11.0.2 (10.11.0.2) (Mozilla/5.0 (X11; Linux x86_64; rv:90.0) Gecko/20100101 Firefox/90.0)
|
||||
2021-09-02 23:56:55 WARNING (MainThread) [homeassistant.bootstrap] Support for the running Python version 3.7.3 is deprecated and will be removed in the first release after December 7, 2020. Please upgrade Python to 3.8.0 or higher.
|
||||
Binary file not shown.
|
|
@ -1,5 +0,0 @@
|
|||
# Use this file to store secrets like usernames and passwords.
|
||||
# Learn more at https://home-assistant.io/docs/configuration/secrets/
|
||||
mqtt_username: mqtt
|
||||
mqtt_password: mqtt_password_placeholder
|
||||
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
user mqtt
|
||||
topic readwrite #
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
per_listener_settings true
|
||||
allow_zero_length_clientid true
|
||||
listener 1883 0.0.0.0
|
||||
allow_anonymous false
|
||||
password_file /mosquitto/config/pwfile
|
||||
acl_file /mosquitto/config/aclfile
|
||||
|
|
@ -1 +0,0 @@
|
|||
mqtt:mqtt_password_placeholder
|
||||
245
README.md
245
README.md
|
|
@ -1,34 +1,234 @@
|
|||
This project is designed to control the GRAFIK Eye QS Control panel via the QSE-CI-NWK-E with the serial interface. The project is designed to use the OLA (https://www.openlighting.org/) project to use a DMX device to a network DMX protocol to control the 6 available zones. This project also supports MQTT messaging for Home Assistant support.
|
||||
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.
|
||||
|
||||
I designed this software for use on a Raspberry Pi using the 2019-07-10-raspbian-buster-lite release and OLA at https://github.com/OpenLightingProject/ola/tree/dc40569a7ef2512c7c9459a94c9dc4292d809262 compiled and installed using instructions at https://www.openlighting.org/ola/linuxinstall/
|
||||
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 with `install-ola.sh` (see below),
|
||||
following the [OLA build guide](https://www.openlighting.org/ola/linuxinstall/).
|
||||
|
||||
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.
|
||||
|
||||
# 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).
|
||||
|
||||
# Overview
|
||||
|
||||
The setup is three steps once the Pi is ready:
|
||||
|
||||
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 (~1–2 h on a Pi Zero).
|
||||
3. **Install the control service** (step 2) and **configure it** (step 3).
|
||||
|
||||
# Installation
|
||||
|
||||
1. Decide if you want Home Assistant support, if you do not, you can skip to step 5.
|
||||
2. If you do not already have an home assistant setup, you can view https://www.home-assistant.io/installation/ or use the base configurations in the `Home Assistant` directory to use my home assistant docker setup. The base configuration is designed for the HUSBZB-1 USB adapter, which you can use `ls -lah /dev/serial/by-id/` to see which ttyUSB interfaces are which, and get the correct serial number for your device.
|
||||
3. If you don't have MQTT setup, you can follow guide at https://cyan-automation.medium.com/setting-up-mqtt-and-mosquitto-in-home-assistant-20eb810a91e6 or use my base configurations in the `Home Assistant` directory to configure mosquitto. Once you setup a username/password in the pwfile, use `mosquitto_passwd -U pwfile` to encrypt the password.
|
||||
4. Edit the `lutron-dmx-control.py` file to make sure MQTT is enabled, pointed to the proper server, and has the correct password configured.
|
||||
5. If you are not planning on using MQTT/Home Assistant, you can edit `lutron-dmx-control.py` to change `MQTT_ENABLED` from True to False.
|
||||
6. Update the serial port for the QSE NWK in `lutron-dmx-control.py`, you can use `ls -lah /dev/serial/by-id/` to determine the device id.
|
||||
7. Run the bash script install.sh to install services for olad and `lutron-dmx-control.py`.
|
||||
## 0. Prepare the Pi and get 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.
|
||||
|
||||
Boot the Pi, then open a terminal on it (directly, or over SSH:
|
||||
`ssh <user>@<pi-address>`). Install git and download this project:
|
||||
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y git
|
||||
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.
|
||||
|
||||
> 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`).
|
||||
|
||||
## 1. Install OLA (only if using DMX)
|
||||
|
||||
If you set `dmx.enabled: false`, skip this step — OLA does not need 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 1–2 hours; the
|
||||
script adds temporary swap on low-memory boards so the compile does not run out of
|
||||
memory.
|
||||
|
||||
```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.
|
||||
|
||||
## 2. Install the control service
|
||||
|
||||
`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.
|
||||
|
||||
```bash
|
||||
sudo bash ./install.sh
|
||||
# or, for a non-pi user:
|
||||
sudo TARGET_USER=james bash ./install.sh
|
||||
```
|
||||
|
||||
# Configuration
|
||||
Once services are installed we need to stop the olad service to edit configuration files with `sudo systemctl start olad@pi`. We can then configure ola by editing the configuration files in `.ola/` to disable the modules which are not used as some of them will take the serial device. Once configured, run `sudo systemctl start olad@pi` and visit the raspberry pi's IP address at port 9090 in your browser to configure the DMX universe you are going to use. Once configured, you can then test this software by changing the configuration portion of the code.
|
||||
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.
|
||||
|
||||
Trick to disable all modules except the one you are using.
|
||||
> 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`.
|
||||
|
||||
## 3. Configure
|
||||
|
||||
Edit `/etc/lutron-dmx-control/config.yaml` (installed from `config.example.yaml`) and set:
|
||||
|
||||
- `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.
|
||||
|
||||
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.
|
||||
|
||||
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`.
|
||||
|
||||
# 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.
|
||||
|
||||
To enable a different/extra plugin, stop olad, flip its config, and restart:
|
||||
|
||||
```bash
|
||||
sed -i '/enabled\s=/c\enabled = false' ~/.ola/*.conf
|
||||
sed -i '/enabled\s=/c\enabled = true' ~/.ola/ola-e131.conf
|
||||
sudo systemctl stop olad@pi
|
||||
sed -i '/^enabled\s*=/c\enabled = true' ~/.ola/ola-artnet.conf # example: also accept Art-Net
|
||||
sudo systemctl start olad@pi
|
||||
```
|
||||
|
||||
# Home Assistant MQTT config
|
||||
## Receiving sACN (patching the universe)
|
||||
|
||||
If you have your own Home Assistant install, the configuration for this project is below.
|
||||
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.
|
||||
|
||||
`install.sh` does this automatically: it patches 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):
|
||||
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.
|
||||
|
||||
> 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.
|
||||
|
||||
# Home Assistant & MQTT (Docker)
|
||||
|
||||
I run Home Assistant and the Mosquitto MQTT broker in Docker via `docker compose`.
|
||||
A minimal `compose.yaml`:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
homeassistant:
|
||||
container_name: home-assistant
|
||||
image: homeassistant/home-assistant:stable
|
||||
volumes:
|
||||
- ./hass:/config
|
||||
environment:
|
||||
- TZ=America/Chicago
|
||||
restart: always
|
||||
network_mode: host
|
||||
mqtt:
|
||||
container_name: mqtt
|
||||
image: eclipse-mosquitto
|
||||
volumes:
|
||||
- ./mosquitto:/mosquitto/config
|
||||
restart: always
|
||||
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`.
|
||||
|
||||
## Mosquitto config
|
||||
|
||||
Mosquitto needs a config and a password in the mounted `./mosquitto` directory.
|
||||
`./mosquitto/mosquitto.conf`:
|
||||
|
||||
```
|
||||
per_listener_settings true
|
||||
allow_zero_length_clientid true
|
||||
listener 1883 0.0.0.0
|
||||
allow_anonymous false
|
||||
password_file /mosquitto/config/pwfile
|
||||
acl_file /mosquitto/config/aclfile
|
||||
```
|
||||
|
||||
`./mosquitto/aclfile` (grant the `mqtt` user full access):
|
||||
|
||||
```
|
||||
user mqtt
|
||||
topic readwrite #
|
||||
```
|
||||
|
||||
Create the password file (use the same `mqtt` user/password you put in
|
||||
`config.yaml`):
|
||||
|
||||
```bash
|
||||
docker compose run --rm mqtt mosquitto_passwd -c -b /mosquitto/config/pwfile mqtt 'your-password'
|
||||
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).
|
||||
|
||||
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:
|
||||
|
||||
```yaml
|
||||
light:
|
||||
|
|
@ -42,18 +242,21 @@ light:
|
|||
supported_color_modes: ["brightness"]
|
||||
```
|
||||
|
||||
# Recommend
|
||||
# Recommended: watchdog
|
||||
|
||||
Enable watchdog on the Raspberry Pi to auto reboot upon system crashes.
|
||||
Enable the hardware watchdog on the Pi to auto-reboot on a system crash.
|
||||
|
||||
Add to `/boot/firmware/config.txt` (or `/boot/config.txt` on older images) under the
|
||||
`[all]` section:
|
||||
|
||||
Edit `/boot/config.txt` and add under the `[all]` section.
|
||||
```
|
||||
watchdog=on
|
||||
```
|
||||
|
||||
Edit `/etc/systemd/system.conf` and uncomment `RuntimeWatchdogSec` and set it as follows.
|
||||
Uncomment `RuntimeWatchdogSec` in `/etc/systemd/system.conf` and set it:
|
||||
|
||||
```
|
||||
RuntimeWatchdogSec=10s
|
||||
```
|
||||
|
||||
After configuring, reboot.
|
||||
Reboot to apply.
|
||||
|
|
|
|||
67
config.example.yaml
Normal file
67
config.example.yaml
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
# Configuration for lutron-dmx-control.
|
||||
#
|
||||
# Copy this file to config.yaml (kept out of git) and edit it:
|
||||
# sudo install -d /etc/lutron-dmx-control
|
||||
# sudo cp config.example.yaml /etc/lutron-dmx-control/config.yaml
|
||||
# sudo chmod 600 /etc/lutron-dmx-control/config.yaml # contains the MQTT password
|
||||
#
|
||||
# The program searches for the config in this order:
|
||||
# 1. --config PATH on the command line
|
||||
# 2. $LUTRON_CONFIG
|
||||
# 3. config.yaml next to the script
|
||||
# 4. ~/.config/lutron-dmx-control/config.yaml
|
||||
# 5. /etc/lutron-dmx-control/config.yaml
|
||||
|
||||
# --- QSE NWK serial link ---
|
||||
serial:
|
||||
# Find yours with: ls -lah /dev/serial/by-id/
|
||||
device: /dev/serial/by-id/usb-Prolific_Technology_Inc._USB-Serial_Controller-if00-port0
|
||||
# Must match the dipswitch on the QSE-CI-NWK-E (9600/19200/38400/115200).
|
||||
baud: 115200
|
||||
|
||||
# --- GRAFIK Eye QS ---
|
||||
qse:
|
||||
integration_id: 1 # Integration ID bound to the GRAFIK Eye main unit
|
||||
zones: 6 # Controllable zones on your model (max 24)
|
||||
fade: "00:00" # Fade sent with each level command; "00:00" = instant (OLA fades)
|
||||
|
||||
# --- DMX (OLA) ---
|
||||
# Optional. Set enabled: false to run without DMX (e.g. MQTT-only control); when
|
||||
# disabled, OLA does not need to be installed.
|
||||
dmx:
|
||||
enabled: true
|
||||
universe: 3
|
||||
start_address: 0 # 0-indexed offset of zone 1 within the universe
|
||||
# While a DMX signal is active, MQTT commands are locked out so DMX stays in
|
||||
# control. This is how long (seconds) after the last DMX universe update the
|
||||
# lockout persists; once it elapses MQTT can drive the lights again.
|
||||
lockout_sec: 5
|
||||
|
||||
# --- Reliability tuning (optional; defaults shown) ---
|
||||
reliability:
|
||||
rx_timeout_sec: 60
|
||||
watchdog_interval_sec: 15
|
||||
reconnect_backoff_min_sec: 1
|
||||
reconnect_backoff_max_sec: 30
|
||||
send_all_interval_sec: 10
|
||||
|
||||
# --- Logging ---
|
||||
logging:
|
||||
level: INFO # DEBUG for verbose TX/RX tracing
|
||||
|
||||
# --- MQTT / Home Assistant ---
|
||||
# Optional. Set enabled: false to disable; when disabled, paho-mqtt does not
|
||||
# need to be installed.
|
||||
mqtt:
|
||||
enabled: true
|
||||
broker: 127.0.0.1
|
||||
port: 1883
|
||||
topic: lutron/qse-nwk
|
||||
username: mqtt
|
||||
password: change-me
|
||||
client_id: null # null -> auto-generated
|
||||
# Publish a Home Assistant MQTT discovery config so the light appears
|
||||
# automatically (no manual configuration.yaml entry needed).
|
||||
discovery: true
|
||||
discovery_prefix: homeassistant
|
||||
device_name: Lutron QSE NWK
|
||||
149
install-ola.sh
Normal file
149
install-ola.sh
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# install-ola.sh - Build and install the Open Lighting Architecture (OLA) from
|
||||
# source on Debian/Raspbian. Verified on Raspberry Pi OS / Raspbian 13 (Trixie)
|
||||
# on a single-core ARMv6 Pi (Pi Zero / Pi 1).
|
||||
#
|
||||
# OLA is not packaged for recent Debian/Raspbian releases, so we build the
|
||||
# 0.10.9 release tag from git. This installs the build dependencies, the C++
|
||||
# daemon (olad) and the Python client bindings (ola.ClientWrapper) that
|
||||
# lutron-dmx-control.py needs when DMX is enabled.
|
||||
#
|
||||
# Usage (run as a normal user that can sudo, or as root):
|
||||
# bash ./install-ola.sh
|
||||
#
|
||||
# Override the version, build location, or parallel-make jobs if needed:
|
||||
# OLA_VERSION=0.10.9 BUILD_DIR=~/ola-build JOBS=4 bash ./install-ola.sh
|
||||
#
|
||||
# Build the OLA docs reference: https://www.openlighting.org/ola/linuxinstall/
|
||||
|
||||
set -e
|
||||
|
||||
OLA_VERSION="${OLA_VERSION:-0.10.9}"
|
||||
BUILD_DIR="${BUILD_DIR:-$HOME/ola-build}"
|
||||
OLA_REPO="${OLA_REPO:-https://github.com/OpenLightingProject/ola.git}"
|
||||
|
||||
# Use sudo only when we are not already root.
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
SUDO=""
|
||||
else
|
||||
SUDO="sudo"
|
||||
if ! command -v sudo >/dev/null 2>&1; then
|
||||
echo "This script needs root (apt + make install). Install sudo or run as root."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "==> Installing build dependencies"
|
||||
$SUDO apt-get update
|
||||
# Dev packages pull in the matching runtime libraries automatically. The Python
|
||||
# client bindings need python3-protobuf and python3-dev.
|
||||
$SUDO apt-get install -y \
|
||||
git build-essential libtool autoconf automake pkg-config \
|
||||
bison flex make g++ \
|
||||
libcppunit-dev uuid-dev zlib1g-dev libncurses5-dev \
|
||||
protobuf-compiler libprotobuf-dev libprotoc-dev \
|
||||
libmicrohttpd-dev libftdi1-dev libusb-1.0-0-dev \
|
||||
libavahi-client-dev \
|
||||
python3 python3-dev python3-protobuf python3-numpy
|
||||
|
||||
# On low-memory boards (e.g. the 512MB Pi Zero) the protobuf-heavy C++ files
|
||||
# exhaust RAM and the compiler is OOM-killed. Add temporary swap for the build
|
||||
# and remove it afterwards. Skipped when there is already plenty of RAM+swap.
|
||||
SWAPFILE="/swapfile-ola-build"
|
||||
ADDED_SWAP=0
|
||||
mem_kb=$(awk '/MemTotal/ {print $2}' /proc/meminfo)
|
||||
swap_kb=$(awk '/SwapTotal/ {print $2}' /proc/meminfo)
|
||||
LOW_MEM=0
|
||||
if [ "$((mem_kb + swap_kb))" -lt 2097152 ]; then
|
||||
LOW_MEM=1
|
||||
if [ ! -f "$SWAPFILE" ]; then
|
||||
echo "==> Low memory detected ($(((mem_kb + swap_kb) / 1024))MB RAM+swap); adding 2G temporary build swap"
|
||||
$SUDO fallocate -l 2G "$SWAPFILE" || $SUDO dd if=/dev/zero of="$SWAPFILE" bs=1M count=2048
|
||||
$SUDO chmod 600 "$SWAPFILE"
|
||||
$SUDO mkswap "$SWAPFILE" >/dev/null
|
||||
$SUDO swapon "$SWAPFILE"
|
||||
ADDED_SWAP=1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Pick the parallel-make job count. On a low-memory board, a full -j<cores> build
|
||||
# of the protobuf-heavy C++ thrashes swap and can still get OOM-killed, so cap it
|
||||
# (a single-core Pi is unaffected -- nproc is 1 there anyway). Override with JOBS=.
|
||||
if [ -n "$JOBS" ]; then
|
||||
:
|
||||
elif [ "$LOW_MEM" -eq 1 ]; then
|
||||
JOBS=$([ "$(nproc)" -gt 2 ] && echo 2 || nproc)
|
||||
else
|
||||
JOBS=$(nproc)
|
||||
fi
|
||||
|
||||
cleanup_swap() {
|
||||
if [ "$ADDED_SWAP" -eq 1 ]; then
|
||||
echo "==> Removing temporary build swap"
|
||||
$SUDO swapoff "$SWAPFILE" 2>/dev/null || true
|
||||
$SUDO rm -f "$SWAPFILE"
|
||||
fi
|
||||
}
|
||||
trap cleanup_swap EXIT
|
||||
|
||||
# If an already-configured tree exists, resume it instead of starting over -
|
||||
# make picks up where it left off. Re-running after an interrupted build (the
|
||||
# Pi is slow; the compile can take 1-2 hours) just continues.
|
||||
if [ -f "$BUILD_DIR/Makefile" ] && [ -f "$BUILD_DIR/config.status" ]; then
|
||||
echo "==> Found a configured build in $BUILD_DIR; resuming"
|
||||
cd "$BUILD_DIR"
|
||||
else
|
||||
echo "==> Fetching OLA $OLA_VERSION into $BUILD_DIR"
|
||||
if [ -d "$BUILD_DIR/.git" ]; then
|
||||
git -C "$BUILD_DIR" fetch --depth 1 origin "refs/tags/$OLA_VERSION:refs/tags/$OLA_VERSION"
|
||||
git -C "$BUILD_DIR" checkout -f "$OLA_VERSION"
|
||||
else
|
||||
rm -rf "$BUILD_DIR"
|
||||
git clone --depth 1 --branch "$OLA_VERSION" "$OLA_REPO" "$BUILD_DIR"
|
||||
fi
|
||||
cd "$BUILD_DIR"
|
||||
|
||||
echo "==> Generating the build system (autoreconf)"
|
||||
autoreconf -i
|
||||
|
||||
# --enable-python-libs: build/install the Python client (ola.ClientWrapper).
|
||||
# --disable-fatal-warnings: OLA 0.10.9 predates GCC 14; without this the new
|
||||
# default warnings are treated as errors (-Werror).
|
||||
# --disable-osc: OLA 0.10.9's OSC plugin uses an old liblo API that
|
||||
# no longer compiles against Trixie's liblo. We do
|
||||
# not use OSC (DMX/sACN only), so disable it.
|
||||
echo "==> Configuring"
|
||||
PYTHON=python3 ./configure --enable-python-libs --disable-fatal-warnings --disable-osc
|
||||
fi
|
||||
|
||||
echo "==> Building with -j$JOBS (this is slow on a single-core Pi; ~1-2 hours on a Pi Zero)"
|
||||
make -j"$JOBS"
|
||||
|
||||
echo "==> Installing"
|
||||
$SUDO make install
|
||||
$SUDO ldconfig
|
||||
|
||||
# OLA installs its Python module to .../site-packages, but Debian's python3 only
|
||||
# searches .../dist-packages, so 'import ola' fails out of the box. Link the
|
||||
# installed package into the dist-packages dir that is actually on sys.path.
|
||||
SITE_OLA=$(find /usr/local/lib/python3*/site-packages -maxdepth 1 -name ola -type d 2>/dev/null | head -1)
|
||||
if [ -n "$SITE_OLA" ] && ! python3 -c "import ola" 2>/dev/null; then
|
||||
PYDIR=$(dirname "$(dirname "$SITE_OLA")")
|
||||
DIST="$PYDIR/dist-packages"
|
||||
echo "==> Linking OLA Python module into $DIST (Debian dist-packages)"
|
||||
$SUDO mkdir -p "$DIST"
|
||||
$SUDO ln -sfn ../site-packages/ola "$DIST/ola"
|
||||
fi
|
||||
|
||||
echo "==> Verifying"
|
||||
olad --version || true
|
||||
if python3 -c "import ola.ClientWrapper" 2>/dev/null; then
|
||||
echo " Python bindings (ola.ClientWrapper) import OK"
|
||||
else
|
||||
echo " WARNING: 'import ola.ClientWrapper' failed - check the install log above."
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "OLA $OLA_VERSION installed. olad lives at $(command -v olad 2>/dev/null || echo /usr/local/bin/olad)."
|
||||
echo "Next: install.sh sets up the olad@<user> and lutron-dmx-control@<user> services."
|
||||
161
install.sh
161
install.sh
|
|
@ -1,29 +1,166 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
USER=$(whoami)
|
||||
if [ "$USER" != "root" ]; then
|
||||
echo "Please use sudo with this install script 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 [ -z "$TARGET_HOME" ]; then
|
||||
echo "Target user '$TARGET_USER' does not exist. Re-run with TARGET_USER=<name> sudo ./install.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# The systemd template (lutron-dmx-control@.service) runs
|
||||
# /home/%i/lutron-dmx-control.py, so it expects the script at /home/$TARGET_USER.
|
||||
# Warn if this user's home is elsewhere -- the service would fail to start.
|
||||
if [ "$TARGET_HOME" != "/home/$TARGET_USER" ]; then
|
||||
echo "WARNING: $TARGET_USER's home is '$TARGET_HOME', but the systemd unit runs"
|
||||
echo " /home/$TARGET_USER/lutron-dmx-control.py. Edit ExecStart in"
|
||||
echo " lutron-dmx-control@.service (and re-copy it) to point at \$TARGET_HOME,"
|
||||
echo " or the service will not start."
|
||||
fi
|
||||
|
||||
# Get the script directory.
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||
cd $SCRIPT_DIR
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
# Install Python/needed modules.
|
||||
apt install -y python3-pip python3-serial python3
|
||||
pip3 install ola
|
||||
pip3 install paho-mqtt
|
||||
# Note: OLA's Python bindings come from the OLA install itself (built from
|
||||
# source per the README), not from PyPI. The PyPI 'ola' package is unrelated.
|
||||
# Refresh the package lists first; on a fresh image the cache may be empty/stale
|
||||
# and the install would otherwise fail with "Unable to locate package".
|
||||
apt-get update
|
||||
apt-get install -y python3-pip python3-serial python3 python3-paho-mqtt python3-yaml
|
||||
|
||||
cp lutron-dmx-control.py /home/pi/lutron-dmx-control.py
|
||||
chown pi: /home/pi/lutron-dmx-control.py
|
||||
# Install the script, but don't clobber an existing one (it may have local edits
|
||||
# prior to the env-file migration).
|
||||
if [ ! -f "$TARGET_HOME/lutron-dmx-control.py" ]; then
|
||||
cp lutron-dmx-control.py "$TARGET_HOME/lutron-dmx-control.py"
|
||||
chown "$TARGET_USER:" "$TARGET_HOME/lutron-dmx-control.py"
|
||||
else
|
||||
echo "$TARGET_HOME/lutron-dmx-control.py already exists; not overwriting."
|
||||
echo " Delete it and re-run to install the new version."
|
||||
fi
|
||||
|
||||
# Copy lutron-dmx-control@.service and olad@.service to /etc/systemd/system/ and run the following to enable/start.
|
||||
# Install config file (only if not already present, to preserve secrets).
|
||||
CONFIG_DIR=/etc/lutron-dmx-control
|
||||
CONFIG_FILE="$CONFIG_DIR/config.yaml"
|
||||
# Owned by the service user so the service (running as $TARGET_USER) can read it.
|
||||
install -d -o "$TARGET_USER" -g "$TARGET_USER" -m 700 "$CONFIG_DIR"
|
||||
# Track whether we just laid down a fresh (unedited) config so we can prompt the
|
||||
# user to edit it before the first start instead of crash-looping on placeholders.
|
||||
NEW_CONFIG=0
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
cp config.example.yaml "$CONFIG_FILE"
|
||||
chown "$TARGET_USER:" "$CONFIG_FILE"
|
||||
chmod 600 "$CONFIG_FILE"
|
||||
NEW_CONFIG=1
|
||||
echo "Installed $CONFIG_FILE - edit before starting the service."
|
||||
else
|
||||
echo "$CONFIG_FILE already exists; leaving in place."
|
||||
fi
|
||||
|
||||
# Copy systemd units.
|
||||
cp olad@.service /etc/systemd/system/
|
||||
cp lutron-dmx-control@.service /etc/systemd/system/
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable olad@pi
|
||||
systemctl start olad@pi
|
||||
systemctl enable lutron-dmx-control@pi
|
||||
systemctl start lutron-dmx-control@pi
|
||||
|
||||
# olad (OLA) is built from source by install-ola.sh and is only needed for DMX.
|
||||
# Set it up when present; MQTT-only setups can run without it.
|
||||
if command -v olad >/dev/null 2>&1; then
|
||||
systemctl enable "olad@$TARGET_USER"
|
||||
systemctl start "olad@$TARGET_USER"
|
||||
|
||||
# Default OLA to network DMX only (E1.31/sACN). Out of the box olad also loads
|
||||
# its serial/USB device plugins, which grab the QSE's serial adapter
|
||||
# (/dev/ttyUSB*) and conflict with this program. Wait for olad to generate its
|
||||
# per-plugin configs, then disable every plugin and re-enable only e131.
|
||||
OLA_DIR="$TARGET_HOME/.ola"
|
||||
for _ in $(seq 1 15); do
|
||||
[ -f "$OLA_DIR/ola-e131.conf" ] && break
|
||||
sleep 1
|
||||
done
|
||||
if [ -f "$OLA_DIR/ola-e131.conf" ]; then
|
||||
systemctl stop "olad@$TARGET_USER"
|
||||
for f in "$OLA_DIR"/ola-*.conf; do
|
||||
# ola-server.conf / ola-universe.conf are not plugin configs.
|
||||
case "$(basename "$f")" in
|
||||
ola-server.conf|ola-universe.conf) continue ;;
|
||||
esac
|
||||
if grep -q '^enabled' "$f"; then
|
||||
sed -i '/^enabled[[:space:]]*=/c\enabled = false' "$f"
|
||||
else
|
||||
printf '\nenabled = false\n' >> "$f"
|
||||
fi
|
||||
done
|
||||
sed -i '/^enabled[[:space:]]*=/c\enabled = true' "$OLA_DIR/ola-e131.conf"
|
||||
chown -R "$TARGET_USER:" "$OLA_DIR"
|
||||
systemctl start "olad@$TARGET_USER"
|
||||
echo "Configured OLA for network DMX only (E1.31/sACN); serial/USB plugins disabled."
|
||||
|
||||
# Patch the E1.31 input port to the DMX universe from config.yaml so olad
|
||||
# actually receives sACN. Registering the universe from the client is not
|
||||
# enough -- without a patched input port olad never joins the sACN
|
||||
# multicast group (sACN universe == OLA universe number). Skipped when DMX
|
||||
# is disabled in the config.
|
||||
UNIVERSE=$(python3 -c "import yaml; d=(yaml.safe_load(open('$CONFIG_FILE')) or {}).get('dmx',{}); print(d.get('universe','') if d.get('enabled', True) else '')" 2>/dev/null)
|
||||
if [ -n "$UNIVERSE" ] && command -v ola_patch >/dev/null 2>&1; then
|
||||
# Wait for olad's RPC + the E1.31 device, then resolve its device id.
|
||||
DEV=""
|
||||
for _ in $(seq 1 10); do
|
||||
DEV=$(ola_dev_info 2>/dev/null | sed -n 's/^Device \([0-9]*\): E1\.31.*/\1/p' | head -1)
|
||||
[ -n "$DEV" ] && break
|
||||
sleep 1
|
||||
done
|
||||
if [ -n "$DEV" ]; then
|
||||
ola_patch --device "$DEV" --port 0 --input --universe "$UNIVERSE" \
|
||||
&& echo "Patched E1.31 input port 0 -> universe $UNIVERSE (sACN reception)."
|
||||
else
|
||||
echo "WARNING: E1.31 device not found; patch universe $UNIVERSE to an E1.31 input port manually (olad web UI :9090)."
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "WARNING: $OLA_DIR/ola-e131.conf not generated; configure OLA plugins manually (see README)."
|
||||
fi
|
||||
else
|
||||
echo "WARNING: 'olad' not found. If you use DMX, run ./install-ola.sh first, then re-run this script."
|
||||
fi
|
||||
|
||||
# Always enable so the service starts on boot. Whether we start it now depends on
|
||||
# whether the config is freshly installed (still has placeholder values).
|
||||
systemctl enable "lutron-dmx-control@$TARGET_USER"
|
||||
|
||||
if [ "$NEW_CONFIG" -eq 1 ]; then
|
||||
# Fresh config: serial.device, MQTT password, etc. are still placeholders, so
|
||||
# starting now would just crash-loop. Walk the user through editing + starting.
|
||||
SVC="lutron-dmx-control@$TARGET_USER"
|
||||
echo
|
||||
echo "============================================================"
|
||||
echo " Almost done. The service is enabled but NOT started yet."
|
||||
echo
|
||||
echo " 1. Edit your config (at minimum: serial.device,"
|
||||
echo " qse.integration_id/zones, and -- if used -- the dmx.* and"
|
||||
echo " mqtt.broker/username/password settings):"
|
||||
echo
|
||||
echo " sudo nano $CONFIG_FILE"
|
||||
echo
|
||||
echo " 2. Start the service:"
|
||||
echo
|
||||
echo " sudo systemctl start $SVC"
|
||||
echo
|
||||
echo " 3. Check it came up cleanly:"
|
||||
echo
|
||||
echo " systemctl status $SVC"
|
||||
echo " journalctl -u $SVC -f"
|
||||
echo "============================================================"
|
||||
else
|
||||
# Existing config: (re)start to pick up the new script/units.
|
||||
systemctl restart "lutron-dmx-control@$TARGET_USER"
|
||||
echo "Restarted lutron-dmx-control@$TARGET_USER."
|
||||
fi
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,12 +1,18 @@
|
|||
[Unit]
|
||||
Description=Lutron DMX Control
|
||||
After=network.target
|
||||
After=network-online.target olad@%i.service
|
||||
Wants=network-online.target
|
||||
StartLimitIntervalSec=300
|
||||
StartLimitBurst=20
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Type=notify
|
||||
NotifyAccess=main
|
||||
Environment=PYTHONUNBUFFERED=1
|
||||
ExecStart=/usr/bin/python3 /home/%I/lutron-dmx-control.py
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
WatchdogSec=120
|
||||
User=%I
|
||||
|
||||
[Install]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue