298 lines
14 KiB
Bash
Executable File
298 lines
14 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# Smart Home Touch-Panel ISO Builder
|
|
# Target: builds a Debian 12 (Bookworm) live ISO on a Debian/Ubuntu build machine
|
|
#
|
|
# Drives `lb config && lb build` over hosts/touch-panel/live-build/ to produce the
|
|
# Sway touch-panel image described in docs/project-plan.md Phase 16:
|
|
# - greetd autologin straight into a kiosk Sway session (no greeter UI)
|
|
# - touchpanel-agent (Python, systemd) — HA MQTT-discovery entities + swaymsg control
|
|
# - workspaces 1:spotify (full Spotify GUI, Flathub) / 2:home (Chromium kiosk
|
|
# pointed at Home Assistant) / 3:web (general Firefox browsing)
|
|
# - an always-visible eww touch dock for on-screen app switching
|
|
# - a toggleable on-screen keyboard (wvkbd)
|
|
#
|
|
# Reuses hosts/thin-client's own live-build toolchain and directory-split convention,
|
|
# NOT its live-build tree — this is a different device (touched directly, not
|
|
# remote-controlled from the couch) with a different app set, so it gets its own
|
|
# smaller config/, agent/, and live-build/, same relationship as
|
|
# hosts/audio-endpoint's amd64 build to the thin client's.
|
|
#
|
|
# This script is also the single point that keeps configs/ (the human-edited source of
|
|
# truth, reviewed in git) in sync with live-build/config/includes.chroot/ (the
|
|
# generated tree that actually gets baked into the image). Never hand-edit anything
|
|
# under includes.chroot — it is wiped and regenerated on every run.
|
|
#
|
|
# Run as: sudo ./build-touch-panel-iso.sh
|
|
#
|
|
# EDIT THE VARIABLES BELOW BEFORE RUNNING.
|
|
|
|
set -euo pipefail
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# CONFIGURATION — edit these before running
|
|
# ---------------------------------------------------------------------------
|
|
DEBIAN_RELEASE="bookworm" # Matches the container host's OS
|
|
KIOSK_USERNAME="kiosk" # The autologin account the whole image is built around
|
|
IMAGE_HOSTNAME="touch-panel" # Hostname baked into the image
|
|
TOUCHPANEL_NAME="Kitchen touch panel" # Friendly name shown on the HA device
|
|
|
|
KEYBOARD_LAYOUT="de" # xkb layout name (`localectl list-x11-keymap-layouts`)
|
|
|
|
ENABLE_INSTALLER="false" # "true" adds a debian-installer to the ISO (install to disk)
|
|
|
|
# --- Where the touch panel talks to -----------------------------------------
|
|
MQTT_BROKER_HOST="192.168.1.10" # <-- EDIT: container-host IP running Mosquitto
|
|
MQTT_BROKER_PORT="1883"
|
|
MQTT_USERNAME="" # Leave empty while Mosquitto runs allow_anonymous
|
|
MQTT_PASSWORD="" # Never commit a real value here
|
|
HA_URL="http://192.168.1.10:8123" # <-- EDIT: Home Assistant URL — the "Home" workspace
|
|
|
|
# Optional: an SSH public key to bake into the kiosk account for out-of-band admin.
|
|
# The image ships with password auth disabled and no wayvnc (see README's scope note),
|
|
# so without this the only admin path is the local console.
|
|
SSH_AUTHORIZED_KEY=""
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Paths
|
|
# ---------------------------------------------------------------------------
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
TOUCH_PANEL_DIR="$(dirname "$SCRIPT_DIR")"
|
|
CONFIGS_DIR="${TOUCH_PANEL_DIR}/configs"
|
|
AGENT_DIR="${TOUCH_PANEL_DIR}/agent"
|
|
LIVE_BUILD_DIR="${TOUCH_PANEL_DIR}/live-build"
|
|
INCLUDES="${LIVE_BUILD_DIR}/config/includes.chroot"
|
|
PACKAGE_LIST="${LIVE_BUILD_DIR}/config/package-lists/touch-panel.list.chroot"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Sanity checks
|
|
# ---------------------------------------------------------------------------
|
|
if [[ $EUID -ne 0 ]]; then
|
|
echo "Warning: not running as root. 'lb build' needs root to bootstrap and chroot,"
|
|
echo " and will fail partway through. Re-run with: sudo $0"
|
|
echo " Continuing anyway so you can at least regenerate includes.chroot..."
|
|
fi
|
|
|
|
if ! grep -qi "debian\|ubuntu" /etc/os-release; then
|
|
echo "Warning: live-build targets a Debian/Ubuntu build host. Proceeding anyway..."
|
|
fi
|
|
|
|
if ! command -v lb &> /dev/null; then
|
|
if [[ $EUID -eq 0 ]]; then
|
|
echo "--- Installing live-build ---"
|
|
apt-get update
|
|
apt-get install -y live-build
|
|
else
|
|
echo "live-build is not installed and this script is not running as root." >&2
|
|
echo " Install it first: sudo apt-get install live-build" >&2
|
|
exit 1
|
|
fi
|
|
else
|
|
echo "--- live-build already installed, skipping ---"
|
|
fi
|
|
|
|
if [[ ! -f "$PACKAGE_LIST" ]]; then
|
|
echo "Missing package list: $PACKAGE_LIST" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "$MQTT_BROKER_HOST" == "192.168.1.10" ]]; then
|
|
echo "Warning: MQTT_BROKER_HOST is still the placeholder IP."
|
|
echo " Edit it at the top of this script to your container host's real LAN address,"
|
|
echo " or the touch panel won't show up as Home Assistant entities."
|
|
fi
|
|
|
|
if [[ "$HA_URL" == "http://192.168.1.10:8123" ]]; then
|
|
echo "Warning: HA_URL is still the placeholder."
|
|
echo " The image builds and boots fine, but the Home workspace will show a"
|
|
echo " connection error until this points at a real Home Assistant."
|
|
fi
|
|
|
|
echo
|
|
echo "=== Smart Home Touch-Panel ISO Builder ==="
|
|
echo "Debian release : $DEBIAN_RELEASE"
|
|
echo "Kiosk user : $KIOSK_USERNAME"
|
|
echo "Image hostname : $IMAGE_HOSTNAME"
|
|
echo "MQTT broker : ${MQTT_BROKER_HOST}:${MQTT_BROKER_PORT}"
|
|
echo "Home Assistant : $HA_URL"
|
|
echo "Keyboard layout : $KEYBOARD_LAYOUT"
|
|
echo
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 1. Regenerate includes.chroot from configs/ and agent/
|
|
# ---------------------------------------------------------------------------
|
|
echo "--- Regenerating $INCLUDES ---"
|
|
rm -rf "$INCLUDES"
|
|
mkdir -p \
|
|
"$INCLUDES/etc/greetd" \
|
|
"$INCLUDES/etc/touchpanel-agent" \
|
|
"$INCLUDES/etc/firefox/policies" \
|
|
"$INCLUDES/etc/touchpanel-firefox" \
|
|
"$INCLUDES/usr/local/bin" \
|
|
"$INCLUDES/opt/touchpanel-agent" \
|
|
"$INCLUDES/home/${KIOSK_USERNAME}/.config/sway" \
|
|
"$INCLUDES/home/${KIOSK_USERNAME}/.config/eww" \
|
|
"$INCLUDES/home/${KIOSK_USERNAME}/.ssh"
|
|
|
|
# @KIOSK_USERNAME@ and @KEYBOARD_LAYOUT@ are the only templated tokens in the configs.
|
|
# Everything else the hooks need is read at build time from
|
|
# /etc/touchpanel-agent/config.env (written below), which live-build copies in
|
|
# (chroot_local-includes) before it runs the hooks (chroot_local-hooks).
|
|
subst() {
|
|
sed -e "s/@KIOSK_USERNAME@/${KIOSK_USERNAME}/g" \
|
|
-e "s/@KEYBOARD_LAYOUT@/${KEYBOARD_LAYOUT}/g" "$1" > "$2"
|
|
}
|
|
|
|
subst "${CONFIGS_DIR}/greetd/config.toml" "$INCLUDES/etc/greetd/config.toml"
|
|
subst "${CONFIGS_DIR}/sway/config" "$INCLUDES/home/${KIOSK_USERNAME}/.config/sway/config"
|
|
subst "${AGENT_DIR}/touchpanel-agent.service" "$INCLUDES/opt/touchpanel-agent/touchpanel-agent.service"
|
|
|
|
install -m 0755 "${CONFIGS_DIR}/greetd/kiosk-session" "$INCLUDES/usr/local/bin/kiosk-session"
|
|
install -m 0755 "${CONFIGS_DIR}/sway/ha-kiosk" "$INCLUDES/usr/local/bin/ha-kiosk"
|
|
install -m 0755 "${CONFIGS_DIR}/spotify/spotify-launch" "$INCLUDES/usr/local/bin/spotify-launch"
|
|
install -m 0755 "${CONFIGS_DIR}/keyboard/toggle-keyboard" "$INCLUDES/usr/local/bin/toggle-keyboard"
|
|
|
|
# Console (VT/TTY) keymap — separate from Sway's own xkb_layout, since greetd briefly
|
|
# owns the console before Sway starts.
|
|
mkdir -p "$INCLUDES/etc/default"
|
|
cat > "$INCLUDES/etc/default/keyboard" <<EOF
|
|
XKBMODEL="pc105"
|
|
XKBLAYOUT="${KEYBOARD_LAYOUT}"
|
|
XKBVARIANT=""
|
|
XKBOPTIONS=""
|
|
|
|
BACKSPACE="guess"
|
|
EOF
|
|
|
|
# Touch dock (eww) — session-scoped files under the kiosk user's own config.
|
|
install -m 0644 "${CONFIGS_DIR}/eww/eww.yuck" "$INCLUDES/home/${KIOSK_USERNAME}/.config/eww/eww.yuck"
|
|
install -m 0644 "${CONFIGS_DIR}/eww/eww.scss" "$INCLUDES/home/${KIOSK_USERNAME}/.config/eww/eww.scss"
|
|
|
|
# Firefox: the general-browsing window launcher, plus the shared chrome/prefs template
|
|
# it copies into its own profile at launch. policies.json is global and goes straight
|
|
# into includes.chroot.
|
|
install -m 0755 "${CONFIGS_DIR}/firefox/web-browser" "$INCLUDES/usr/local/bin/web-browser"
|
|
install -m 0644 "${CONFIGS_DIR}/firefox/policies.json" "$INCLUDES/etc/firefox/policies/policies.json"
|
|
install -m 0644 "${CONFIGS_DIR}/firefox/userChrome.css" "$INCLUDES/etc/touchpanel-firefox/userChrome.css"
|
|
install -m 0644 "${CONFIGS_DIR}/firefox/user.js" "$INCLUDES/etc/touchpanel-firefox/user.js"
|
|
|
|
cp -r "${AGENT_DIR}/touchpanel_agent" "$INCLUDES/opt/touchpanel-agent/"
|
|
install -m 0644 "${AGENT_DIR}/requirements.txt" "$INCLUDES/opt/touchpanel-agent/requirements.txt"
|
|
find "$INCLUDES/opt/touchpanel-agent" -name '__pycache__' -type d -prune -exec rm -rf {} +
|
|
|
|
if [[ -n "$SSH_AUTHORIZED_KEY" ]]; then
|
|
echo "$SSH_AUTHORIZED_KEY" > "$INCLUDES/home/${KIOSK_USERNAME}/.ssh/authorized_keys"
|
|
chmod 600 "$INCLUDES/home/${KIOSK_USERNAME}/.ssh/authorized_keys"
|
|
echo " Baked an SSH authorized_keys entry for ${KIOSK_USERNAME}."
|
|
else
|
|
echo " No SSH_AUTHORIZED_KEY set — SSH admin access will not be possible on this image."
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 2. Runtime config, read by touchpanel-agent, the sway session wrapper, and hooks
|
|
# ---------------------------------------------------------------------------
|
|
echo "--- Writing /etc/touchpanel-agent/config.env into includes.chroot ---"
|
|
cat > "$INCLUDES/etc/touchpanel-agent/config.env" <<EOF
|
|
# Generated by hosts/touch-panel/scripts/build-touch-panel-iso.sh — do not hand-edit
|
|
# here; edit the CONFIGURATION block in that script and rebuild.
|
|
KIOSK_USERNAME=${KIOSK_USERNAME}
|
|
TOUCHPANEL_NAME=${TOUCHPANEL_NAME}
|
|
|
|
MQTT_BROKER_HOST=${MQTT_BROKER_HOST}
|
|
MQTT_BROKER_PORT=${MQTT_BROKER_PORT}
|
|
MQTT_USERNAME=${MQTT_USERNAME}
|
|
MQTT_PASSWORD=${MQTT_PASSWORD}
|
|
|
|
HA_URL=${HA_URL}
|
|
EOF
|
|
chmod 0644 "$INCLUDES/etc/touchpanel-agent/config.env"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 3. lb config
|
|
# ---------------------------------------------------------------------------
|
|
cd "$LIVE_BUILD_DIR"
|
|
|
|
chmod +x config/hooks/normal/*.hook.chroot
|
|
|
|
if [[ -e .build ]]; then
|
|
echo "--- Previous build found, running 'lb clean' (package cache is kept) ---"
|
|
lb clean
|
|
fi
|
|
|
|
INSTALLER_MODE="none"
|
|
if [[ "$ENABLE_INSTALLER" == "true" ]]; then
|
|
INSTALLER_MODE="live"
|
|
fi
|
|
|
|
echo "--- Running lb config ---"
|
|
lb config \
|
|
--distribution "$DEBIAN_RELEASE" \
|
|
--architectures amd64 \
|
|
--linux-flavours amd64 \
|
|
--archive-areas "main contrib non-free-firmware" \
|
|
--binary-images iso-hybrid \
|
|
--debian-installer "$INSTALLER_MODE" \
|
|
--iso-application "SmartestHome Touch Panel" \
|
|
--iso-publisher "SmartestHome" \
|
|
--iso-volume "smarthome-touch-panel" \
|
|
--memtest none \
|
|
--bootappend-live "boot=live components quiet splash noautologin username=${KIOSK_USERNAME} hostname=${IMAGE_HOSTNAME}"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 4. lb build
|
|
# ---------------------------------------------------------------------------
|
|
echo "--- Running lb build (this takes a while and needs network) ---"
|
|
lb build
|
|
|
|
ISO_PATH="$(find "$LIVE_BUILD_DIR" -maxdepth 1 -name '*.iso' -print -quit)"
|
|
ISO_PATH="${ISO_PATH:-${LIVE_BUILD_DIR}/live-image-amd64.hybrid.iso}"
|
|
|
|
echo
|
|
echo "=== Done ==="
|
|
echo "ISO written to:"
|
|
echo " ${ISO_PATH}"
|
|
echo
|
|
echo "What's in it:"
|
|
echo " greetd : autologin as '${KIOSK_USERNAME}' straight into Sway on vt1"
|
|
echo " Sway : workspaces 1:spotify / 2:home / 3:web, always-on touch dock"
|
|
echo " touchpanel-agent : system service, MQTT ${MQTT_BROKER_HOST}:${MQTT_BROKER_PORT}"
|
|
echo " Spotify : full GUI client (Flathub com.spotify.Client), workspace 1"
|
|
echo " Home Assistant : Chromium kiosk, ${HA_URL}, workspace 2, auto-restart-on-crash"
|
|
echo " Web browser : Firefox, minimal chrome, uBlock Origin + SponsorBlock, workspace 3"
|
|
echo " Touch dock : bottom bar (eww) — Spotify / Home / Web / Keyboard buttons"
|
|
echo " On-screen keyboard: wvkbd, toggled from the dock's Keyboard button"
|
|
echo " Keyboard layout : ${KEYBOARD_LAYOUT} (console + Sway)"
|
|
echo " Maintenance shell : Super+Shift+Ctrl+M opens a floating terminal locally"
|
|
echo " Remote admin : SSH only — no wayvnc on this image, see README"
|
|
echo
|
|
echo "Next steps:"
|
|
echo " 1. Write the ISO to a USB stick:"
|
|
echo " sudo dd if=${ISO_PATH} of=/dev/sdX bs=4M status=progress oflag=sync"
|
|
echo " (double-check /dev/sdX with 'lsblk' first — dd will happily eat the wrong disk)"
|
|
echo " 2. Boot the target touchscreen machine from it. No touch-panel hardware has been"
|
|
echo " chosen yet (see the README) — confirm touch input is recognised as a Wayland"
|
|
echo " wl_touch device (not emulated as a pointer) before treating this as validated."
|
|
echo " 3. Confirm greetd autologin lands in Sway: dark background, the touch dock at"
|
|
echo " the bottom, and the Home Assistant kiosk window on workspace 2:home."
|
|
echo " 4. Tap through the dock: Spotify (log in on first launch — Premium required),"
|
|
echo " Home, Web."
|
|
echo " 5. Confirm touchpanel-agent connected and registered:"
|
|
echo " systemctl status touchpanel-agent"
|
|
echo " In Home Assistant, a '${TOUCHPANEL_NAME}' device should appear under the MQTT"
|
|
echo " integration with: Show Spotify / Show Home / Show web browser (buttons),"
|
|
echo " Screen (select), Volume (number), Playback state (sensor), and"
|
|
echo " play/pause/next/prev/stop buttons."
|
|
echo " 6. Start something in Spotify and confirm Playback state follows it (playerctl"
|
|
echo " -> touchpanel-agent's MPRIS bridge — needs Spotify's own MPRIS bus name to"
|
|
echo " actually be 'spotify', unverified against the real Flatpak build)."
|
|
echo " 7. Tap the dock's Keyboard button over a text field (Spotify search, the browser"
|
|
echo " address bar) and confirm wvkbd shows/hides. There is no auto-show — see README."
|
|
echo
|
|
echo "Then pull the power on the container host and re-check: the panel must still boot"
|
|
echo "and show Spotify/the browser (Home will show a connection error, which is"
|
|
echo "expected) with the container host powered off — same reactive-path philosophy as"
|
|
echo "hosts/thin-client's Phase 11.10."
|
|
echo
|
|
echo "Rebuilding later: edit hosts/touch-panel/configs/* or agent/*, then re-run this"
|
|
echo "script — includes.chroot is regenerated from them every time."
|