SmartestHome/hosts/kitchen-display/scripts/build-kitchen-display-iso.sh

266 lines
11 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# Smart Home Kitchen-Display ISO Builder
# Target: builds a Debian 12 (Bookworm) live ISO on a Debian/Ubuntu build machine
#
# Drives `lb config && lb build` over hosts/kitchen-display/live-build/ to produce
# the fridge/pantry camera-cataloguing display described in docs/project-plan.md
# Phase 17:
# - greetd autologin straight into a kiosk Sway session (no greeter UI)
# - ONE Chromium kiosk window pointed at pantry-web's frontend (Scan / Inventory /
# Recipes is in-page tab navigation, not separate Sway workspaces)
# - kitchen-display-agent (Python, systemd) — HA MQTT "Show scan/inventory/recipes"
# buttons only; the actual camera capture and Grocy read/write path is
# pantry-vision's own published API, called directly by the browser
#
# Reuses hosts/thin-client's live-build toolchain and directory-split convention, not
# its live-build tree — same relationship hosts/touch-panel and hosts/audio-endpoint
# already have to the thin client's.
#
# Run as: sudo ./build-kitchen-display-iso.sh
#
# EDIT THE VARIABLES BELOW BEFORE RUNNING.
set -euo pipefail
# ---------------------------------------------------------------------------
# CONFIGURATION — edit these before running
# ---------------------------------------------------------------------------
DEBIAN_RELEASE="bookworm"
KIOSK_USERNAME="kiosk"
IMAGE_HOSTNAME="kitchen-display"
KITCHEN_DISPLAY_NAME="Kitchen fridge display"
KEYBOARD_LAYOUT="de"
ENABLE_INSTALLER="false"
# --- Where the kitchen display talks to -------------------------------------
MQTT_BROKER_HOST="192.168.1.10" # <-- EDIT: container-host IP running Mosquitto
MQTT_BROKER_PORT="1883"
MQTT_USERNAME=""
MQTT_PASSWORD=""
# pantry-web (nginx, serves pantry-vision/frontend/) and pantry-vision (the API the
# frontend calls directly from the browser) — both from ENABLE_PANTRY_VISION in
# setup-container-host.sh. Placeholders until that's deployed; the image builds and
# boots fine without it, the kiosk window just shows a connection error.
PANTRY_WEB_URL="http://192.168.1.10:8096" # <-- EDIT once pantry-web is deployed
PANTRY_VISION_URL="http://192.168.1.10:8095" # <-- EDIT once pantry-vision is deployed
# Same value as PANTRY_VISION_TOKEN in pantry-vision/pantry-vision.env — there is no
# way for this repo to push it between the two hosts for you, same as every other
# credential pair that spans two machines in this project.
PANTRY_VISION_TOKEN="" # <-- EDIT: must match pantry-vision's own token
SSH_AUTHORIZED_KEY=""
# ---------------------------------------------------------------------------
# Paths
# ---------------------------------------------------------------------------
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
KITCHEN_DISPLAY_DIR="$(dirname "$SCRIPT_DIR")"
CONFIGS_DIR="${KITCHEN_DISPLAY_DIR}/configs"
AGENT_DIR="${KITCHEN_DISPLAY_DIR}/agent"
LIVE_BUILD_DIR="${KITCHEN_DISPLAY_DIR}/live-build"
INCLUDES="${LIVE_BUILD_DIR}/config/includes.chroot"
PACKAGE_LIST="${LIVE_BUILD_DIR}/config/package-lists/kitchen-display.list.chroot"
# ---------------------------------------------------------------------------
# Sanity checks
# ---------------------------------------------------------------------------
if [[ $EUID -ne 0 ]]; then
echo "Warning: not running as root. 'lb build' needs root; re-run with: sudo $0"
echo " Continuing anyway so you can at least regenerate includes.chroot..."
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 — edit it before building."
fi
if [[ -z "$PANTRY_VISION_TOKEN" ]]; then
echo "Warning: PANTRY_VISION_TOKEN is empty. The kiosk window will load but every"
echo " call to pantry-vision will fail (401) until this matches the token in"
echo " pantry-vision/pantry-vision.env on the container host."
fi
echo
echo "=== Smart Home Kitchen-Display 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 "pantry-web : $PANTRY_WEB_URL"
echo "pantry-vision : $PANTRY_VISION_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/kitchen-display-agent" \
"$INCLUDES/usr/local/bin" \
"$INCLUDES/opt/kitchen-display-agent" \
"$INCLUDES/home/${KIOSK_USERNAME}/.config/sway" \
"$INCLUDES/home/${KIOSK_USERNAME}/.ssh"
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}/kitchen-display-agent.service" "$INCLUDES/opt/kitchen-display-agent/kitchen-display-agent.service"
install -m 0755 "${CONFIGS_DIR}/greetd/kiosk-session" "$INCLUDES/usr/local/bin/kiosk-session"
install -m 0755 "${CONFIGS_DIR}/sway/pantry-kiosk" "$INCLUDES/usr/local/bin/pantry-kiosk"
mkdir -p "$INCLUDES/etc/default"
cat > "$INCLUDES/etc/default/keyboard" <<EOF
XKBMODEL="pc105"
XKBLAYOUT="${KEYBOARD_LAYOUT}"
XKBVARIANT=""
XKBOPTIONS=""
BACKSPACE="guess"
EOF
cp -r "${AGENT_DIR}/kitchen_display_agent" "$INCLUDES/opt/kitchen-display-agent/"
install -m 0644 "${AGENT_DIR}/requirements.txt" "$INCLUDES/opt/kitchen-display-agent/requirements.txt"
find "$INCLUDES/opt/kitchen-display-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
# ---------------------------------------------------------------------------
echo "--- Writing /etc/kitchen-display-agent/config.env into includes.chroot ---"
cat > "$INCLUDES/etc/kitchen-display-agent/config.env" <<EOF
# Generated by hosts/kitchen-display/scripts/build-kitchen-display-iso.sh — do not
# hand-edit here; edit the CONFIGURATION block in that script and rebuild.
KIOSK_USERNAME=${KIOSK_USERNAME}
KITCHEN_DISPLAY_NAME=${KITCHEN_DISPLAY_NAME}
MQTT_BROKER_HOST=${MQTT_BROKER_HOST}
MQTT_BROKER_PORT=${MQTT_BROKER_PORT}
MQTT_USERNAME=${MQTT_USERNAME}
MQTT_PASSWORD=${MQTT_PASSWORD}
PANTRY_WEB_URL=${PANTRY_WEB_URL}
PANTRY_VISION_URL=${PANTRY_VISION_URL}
PANTRY_VISION_TOKEN=${PANTRY_VISION_TOKEN}
EOF
chmod 0644 "$INCLUDES/etc/kitchen-display-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 Kitchen Display" \
--iso-publisher "SmartestHome" \
--iso-volume "smarthome-kitchen-display" \
--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 : one workspace, one Chromium kiosk window"
echo " Kiosk page : ${PANTRY_WEB_URL}/index.html (Scan / Inventory / Recipes tabs)"
echo " Camera : getUserMedia in Chromium (--use-fake-ui-for-media-stream"
echo " auto-accepts the permission prompt), not a native capture app"
echo " kitchen-display-agent: system service, MQTT ${MQTT_BROKER_HOST}:${MQTT_BROKER_PORT}"
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, same scope decision as hosts/touch-panel"
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)"
echo " 2. Boot the target machine — a webcam pointed at the counter/fridge front is"
echo " required hardware, not built in; see the README for what's assumed about it."
echo " 3. Confirm greetd autologin lands in Sway with the Chromium kiosk window up."
echo " 4. Grant/verify camera access: --use-fake-ui-for-media-stream should skip the"
echo " prompt entirely; if the Scan tab shows a camera error, check"
echo " 'v4l2-ctl --list-devices' and that Chromium picked the right one (Chromium"
echo " defaults to the system default video input — untested on real hardware)."
echo " 5. Confirm kitchen-display-agent connected and registered:"
echo " systemctl status kitchen-display-agent"
echo " In Home Assistant, a '${KITCHEN_DISPLAY_NAME}' device should appear under the"
echo " MQTT integration with: Show scan / Show inventory / Show recipes (buttons)."
echo " 6. Hold a grocery item up to the camera, tap Capture, and confirm a proposal"
echo " comes back from pantry-vision within a few seconds — first checkpoint for"
echo " whether the chosen Ollama vision model's latency is workable at all."
echo
echo "Then pull the power on the container host and re-check: the kiosk must still"
echo "boot (Scan/Inventory/Recipes will show connection errors, which is the expected"
echo "degraded state — this device has no local fallback data source, same acceptance"
echo "already documented for hosts/touch-panel's Home workspace)."
echo
echo "Rebuilding later: edit hosts/kitchen-display/configs/* or agent/*, then re-run"
echo "this script — includes.chroot is regenerated from them every time."