#!/usr/bin/env bash # Vigilar Kiosk — Full Raspberry Pi setup script # Run on a fresh Raspberry Pi OS Lite (64-bit, no desktop). # Safe to re-run (idempotent). set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" KIOSK_USER="vigilar" KIOSK_HOME="/home/${KIOSK_USER}" CONFIG_FILE="${KIOSK_HOME}/kiosk_config.txt" # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- info() { printf '\n\033[1;34m>>> %s\033[0m\n' "$*"; } warn() { printf '\033[1;33mWARN: %s\033[0m\n' "$*"; } error() { printf '\033[1;31mERROR: %s\033[0m\n' "$*" >&2; exit 1; } need_root() { if [[ $EUID -ne 0 ]]; then error "This script must be run as root (use sudo)." fi } # --------------------------------------------------------------------------- # Pre-flight # --------------------------------------------------------------------------- need_root info "Vigilar Kiosk Setup" echo "This script turns a Raspberry Pi into a dedicated security-camera display." echo "" # Detect Debian version if [[ -f /etc/os-release ]]; then # shellcheck source=/dev/null . /etc/os-release DEBIAN_VERSION="${VERSION_ID:-11}" else DEBIAN_VERSION="11" fi info "Detected Debian version: ${DEBIAN_VERSION} (${PRETTY_NAME:-unknown})" # --------------------------------------------------------------------------- # 1. Ask for Vigilar server URL # --------------------------------------------------------------------------- DEFAULT_URL="https://vigilar.local:49735/kiosk/" read -r -p "Vigilar server URL [${DEFAULT_URL}]: " USER_URL VIGILAR_URL="${USER_URL:-$DEFAULT_URL}" # --------------------------------------------------------------------------- # 2. Ask for display settings # --------------------------------------------------------------------------- read -r -p "Screen rotation (0/90/180/270) [0]: " USER_ROTATION ROTATION="${USER_ROTATION:-0}" read -r -p "Screen resolution (e.g. 1920x1080, 1280x720) [1920x1080]: " USER_RES RESOLUTION="${USER_RES:-1920x1080}" echo "" info "Configuration summary" echo " URL: ${VIGILAR_URL}" echo " Rotation: ${ROTATION}" echo " Resolution: ${RESOLUTION}" echo "" read -r -p "Continue? [Y/n]: " CONFIRM if [[ "${CONFIRM,,}" == "n" ]]; then echo "Aborted." exit 0 fi # --------------------------------------------------------------------------- # 3. Install packages # --------------------------------------------------------------------------- info "Updating package lists" apt-get update -qq info "Installing X11, Chromium, and utilities" PACKAGES=( xserver-xorg xinit x11-xserver-utils chromium-browser unclutter ) # On Bookworm the package may just be 'chromium' if [[ "${DEBIAN_VERSION}" -ge 12 ]]; then # Try chromium-browser first; fall back to chromium if ! apt-cache show chromium-browser &>/dev/null; then PACKAGES=("${PACKAGES[@]/chromium-browser/chromium}") fi fi apt-get install -y -qq "${PACKAGES[@]}" # --------------------------------------------------------------------------- # 4. Create kiosk user # --------------------------------------------------------------------------- if id "${KIOSK_USER}" &>/dev/null; then info "User '${KIOSK_USER}' already exists" else info "Creating user '${KIOSK_USER}'" useradd -m -s /bin/bash -G video,input,render,tty "${KIOSK_USER}" fi # Ensure group memberships even if user existed usermod -aG video,input,render,tty "${KIOSK_USER}" # --------------------------------------------------------------------------- # 5. Write config file # --------------------------------------------------------------------------- info "Writing ${CONFIG_FILE}" cat > "${CONFIG_FILE}" < "${KIOSK_HOME}/.xinitrc" <<'XINITEOF' #!/usr/bin/env bash # Vigilar kiosk X session set -euo pipefail CONFIG="$HOME/kiosk_config.txt" if [[ -f "$CONFIG" ]]; then # shellcheck source=/dev/null source "$CONFIG" fi URL="${VIGILAR_URL:-https://vigilar.local:49735/kiosk/}" # Disable screen blanking and DPMS xset s off xset s noblank xset -dpms # Hide cursor after 0.5 s of inactivity unclutter -idle 0.5 -root & # Handle rotation ROTATION="${ROTATION:-0}" case "${ROTATION}" in 90) xrandr --output "$(xrandr | grep ' connected' | head -1 | awk '{print $1}')" --rotate left ;; 180) xrandr --output "$(xrandr | grep ' connected' | head -1 | awk '{print $1}')" --rotate inverted ;; 270) xrandr --output "$(xrandr | grep ' connected' | head -1 | awk '{print $1}')" --rotate right ;; *) ;; # 0 = normal, no action needed esac # Determine chromium binary CHROMIUM="" for candidate in chromium-browser chromium; do if command -v "$candidate" &>/dev/null; then CHROMIUM="$candidate" break fi done if [[ -z "$CHROMIUM" ]]; then echo "ERROR: No chromium binary found" >&2 exit 1 fi # Launch Chromium in kiosk mode # --ignore-certificate-errors is needed for self-signed TLS certs on vigilar.local exec "$CHROMIUM" \ --noerrdialogs \ --disable-infobars \ --kiosk \ --incognito \ --disable-translate \ --no-first-run \ --fast \ --fast-start \ --disable-features=TranslateUI \ --disk-cache-dir=/dev/null \ --check-for-update-interval=31536000 \ --ignore-certificate-errors \ --disable-component-update \ --disable-background-networking \ --disable-sync \ --autoplay-policy=no-user-gesture-required \ "$URL" XINITEOF chmod +x "${KIOSK_HOME}/.xinitrc" chown "${KIOSK_USER}:${KIOSK_USER}" "${KIOSK_HOME}/.xinitrc" # --------------------------------------------------------------------------- # 7. Write .bash_profile for auto-start (fallback method) # --------------------------------------------------------------------------- info "Writing ${KIOSK_HOME}/.bash_profile (auto-start X on tty1)" # Preserve existing content if any BASH_PROFILE="${KIOSK_HOME}/.bash_profile" MARKER="# --- Vigilar kiosk auto-start ---" # Remove old block if present if [[ -f "${BASH_PROFILE}" ]]; then sed -i "/${MARKER}/,/# --- end Vigilar kiosk ---/d" "${BASH_PROFILE}" fi cat >> "${BASH_PROFILE}" <<'BPEOF' # --- Vigilar kiosk auto-start --- if [[ -z "${DISPLAY:-}" ]] && [[ "$(tty)" == "/dev/tty1" ]]; then exec startx -- -nocursor fi # --- end Vigilar kiosk --- BPEOF chown "${KIOSK_USER}:${KIOSK_USER}" "${BASH_PROFILE}" # --------------------------------------------------------------------------- # 8. Install systemd service (default method) # --------------------------------------------------------------------------- info "Installing systemd service" SERVICE_SRC="${SCRIPT_DIR}/kiosk.service" SERVICE_DEST="/etc/systemd/system/vigilar-kiosk.service" if [[ -f "${SERVICE_SRC}" ]]; then cp "${SERVICE_SRC}" "${SERVICE_DEST}" else # Generate inline if source file not found cat > "${SERVICE_DEST}" < "${AUTOLOGIN_DIR}/autologin.conf" <> "${CONFIG_TXT}" fi fi # --------------------------------------------------------------------------- # 12. Screen rotation and resolution in config.txt # --------------------------------------------------------------------------- info "Configuring display rotation and resolution" if [[ -f "${CONFIG_TXT}" ]]; then # Rotation via display_rotate (legacy) or display_lcd_rotate if [[ "${ROTATION}" != "0" ]]; then ROTATE_VAL=0 case "${ROTATION}" in 90) ROTATE_VAL=1 ;; 180) ROTATE_VAL=2 ;; 270) ROTATE_VAL=3 ;; esac if grep -q '^display_rotate=' "${CONFIG_TXT}"; then sed -i "s/^display_rotate=.*/display_rotate=${ROTATE_VAL}/" "${CONFIG_TXT}" else echo "display_rotate=${ROTATE_VAL}" >> "${CONFIG_TXT}" fi fi # Resolution via hdmi_group and hdmi_mode # Parse WxH RES_W="${RESOLUTION%%x*}" RES_H="${RESOLUTION##*x}" HDMI_MODE="" case "${RES_W}x${RES_H}" in 1920x1080) HDMI_MODE=16 ;; 1280x720) HDMI_MODE=4 ;; 1680x1050) HDMI_MODE=58 ;; 1280x1024) HDMI_MODE=35 ;; 1024x768) HDMI_MODE=16 ;; # DMT mode 16 *) warn "Unknown resolution ${RESOLUTION}, skipping hdmi_mode config" ;; esac if [[ -n "${HDMI_MODE}" ]]; then # hdmi_group=1 = CEA (TVs), hdmi_group=2 = DMT (monitors) HDMI_GROUP=1 if [[ "${RES_W}x${RES_H}" == "1680x1050" ]] || [[ "${RES_W}x${RES_H}" == "1280x1024" ]]; then HDMI_GROUP=2 fi for KEY_VAL in "hdmi_group=${HDMI_GROUP}" "hdmi_mode=${HDMI_MODE}"; do KEY="${KEY_VAL%%=*}" if grep -q "^${KEY}=" "${CONFIG_TXT}"; then sed -i "s/^${KEY}=.*/${KEY_VAL}/" "${CONFIG_TXT}" else echo "${KEY_VAL}" >> "${CONFIG_TXT}" fi done fi fi # --------------------------------------------------------------------------- # 13. Enable SSH # --------------------------------------------------------------------------- info "Enabling SSH" systemctl enable ssh 2>/dev/null || systemctl enable sshd 2>/dev/null || true systemctl start ssh 2>/dev/null || systemctl start sshd 2>/dev/null || true # --------------------------------------------------------------------------- # 14. Set hostname # --------------------------------------------------------------------------- info "Setting hostname to 'vigilar-kiosk'" hostnamectl set-hostname vigilar-kiosk 2>/dev/null || { echo "vigilar-kiosk" > /etc/hostname sed -i "s/127\.0\.1\.1.*/127.0.1.1\tvigilar-kiosk/" /etc/hosts } # --------------------------------------------------------------------------- # Done # --------------------------------------------------------------------------- info "Setup complete!" echo "" echo " Kiosk URL: ${VIGILAR_URL}" echo " User: ${KIOSK_USER}" echo " Service: vigilar-kiosk.service (systemd, enabled)" echo " Config: ${CONFIG_FILE}" echo "" echo " To start now: sudo systemctl start vigilar-kiosk" echo " To check status: sudo systemctl status vigilar-kiosk" echo " To view logs: sudo journalctl -u vigilar-kiosk -f" echo " To reconfigure: sudo ./update_kiosk.sh" echo "" echo " A reboot is recommended: sudo reboot"