lutron-dmx-control/install-ola.sh
James Coleman 21fe3a1476 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).
2026-06-01 17:46:58 -05:00

149 lines
5.9 KiB
Bash

#!/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."