Files
AvianVisitors/frame/install-interactive.sh
T
Chris Sader 6884cce3c7 Add configurable title, matte dimensions, and interactive installer
- Configurable collage title via localStorage (bird:title) with admin settings UI
- Configurable matte opening dimensions in display.py (mat_opening_w/mat_opening_h)
- Interactive frame installer (install-interactive.sh) with guided setup prompts

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-10 15:53:30 -05:00

241 lines
6.8 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# Interactive installer for the AvianVisitors e-ink frame.
# Walks you through configuration with prompts and sensible defaults,
# then hands off to install.sh with the right flags.
set -euo pipefail
cd "$(dirname "$0")"
# --- helpers ----------------------------------------------------------------
ask() {
local prompt="$1" default="$2" var="$3"
if [ -n "$default" ]; then
printf '%s [%s]: ' "$prompt" "$default" >&2
else
printf '%s: ' "$prompt" >&2
fi
read -r input
eval "$var=\"\${input:-\$default}\""
}
pick() {
local prompt="$1" var="$2"
shift 2
echo "" >&2
echo "$prompt" >&2
local i=1
for opt in "$@"; do
printf ' %d) %s\n' "$i" "$opt" >&2
i=$((i + 1))
done
printf 'Choice [1]: ' >&2
read -r choice
choice="${choice:-1}"
eval "$var=\"$choice\""
}
confirm() {
printf '%s [Y/n]: ' "$1" >&2
read -r yn
case "$yn" in
[Nn]*) return 1 ;;
*) return 0 ;;
esac
}
hr() { echo "────────────────────────────────────────────────" >&2; }
# --- start ------------------------------------------------------------------
echo ""
echo " ╔═══════════════════════════════════════╗"
echo " ║ AvianVisitors e-ink frame setup ║"
echo " ╚═══════════════════════════════════════╝"
echo ""
# --- mode -------------------------------------------------------------------
pick "How will the frame get its birds?" MODE_CHOICE \
"Local BirdNET-Pi on my network (default)" \
"BirdWeather — no mic, uses nearby stations" \
"Image URL — fetch a pre-rendered frame PNG"
INSTALL_FLAGS=""
MODE="local"
BASE_URL=""
ZIP=""
IMAGE_URL=""
EBIRD_KEY=""
case "$MODE_CHOICE" in
2)
MODE="birdweather"
echo "" >&2
ask "ZIP / postal code" "" ZIP
if [ -z "$ZIP" ]; then
echo "A ZIP code is required for BirdWeather mode." >&2
exit 1
fi
INSTALL_FLAGS="--bird-weather --zip $ZIP"
echo "" >&2
if confirm "Do you have an eBird API key? (helps if no BirdWeather stations nearby)"; then
ask "eBird API key" "" EBIRD_KEY
if [ -n "$EBIRD_KEY" ]; then
INSTALL_FLAGS="$INSTALL_FLAGS --ebird-key $EBIRD_KEY"
fi
fi
;;
3)
MODE="image"
echo "" >&2
ask "Frame image URL" "" IMAGE_URL
if [ -z "$IMAGE_URL" ]; then
echo "A URL is required for image mode." >&2
exit 1
fi
INSTALL_FLAGS="--image-url $IMAGE_URL"
;;
*)
MODE="local"
echo "" >&2
ask "BirdNET-Pi address" "birdnet.local" BASE_URL
;;
esac
# --- frame & matte ----------------------------------------------------------
hr
pick "What frame and matte are you using?" FRAME_CHOICE \
"A4 frame with A5 matte (stock kit)" \
"11×14 frame with 8×10 matte" \
"Full panel, no matte" \
"Custom dimensions"
MAT_W=0
MAT_H=0
case "$FRAME_CHOICE" in
2) MAT_W=8; MAT_H=10 ;;
3) MAT_W=8.4; MAT_H=11.2 ;;
4)
echo "" >&2
echo "Enter the matte opening size (the visible area, in inches):" >&2
ask " Opening width" "8" MAT_W
ask " Opening height" "10" MAT_H
;;
*) MAT_W=0; MAT_H=0 ;;
esac
# --- titles -----------------------------------------------------------------
hr
echo "" >&2
TITLE="Avian Visitors"
SUBTITLE="Heard Today"
if confirm "Customize the frame title? (default: \"Avian Visitors\" / \"Heard Today\")"; then
ask "Title (top line)" "Avian Visitors" TITLE
ask "Subtitle (large text)" "Heard Today" SUBTITLE
fi
# --- rotation ---------------------------------------------------------------
hr
pick "Which way does the display hang?" ROTATE_CHOICE \
"USB cable exits bottom-right (rotate = 90)" \
"USB cable exits top-left (rotate = 270)"
ROTATE=90
[ "$ROTATE_CHOICE" = "2" ] && ROTATE=270
# --- quiet hours ------------------------------------------------------------
hr
QUIET_START=0
QUIET_END=0
if confirm "Set quiet hours? (skip refreshes overnight to avoid e-ink noise)"; then
ask "Quiet start hour (0-23)" "22" QUIET_START
ask "Quiet end hour (0-23)" "6" QUIET_END
fi
# --- basic auth -------------------------------------------------------------
hr
BASIC_USER=""
BASIC_PASS=""
if [ "$MODE" = "local" ]; then
if confirm "Is your BirdNET-Pi behind basic auth?"; then
ask "Username" "" BASIC_USER
ask "Password" "" BASIC_PASS
fi
fi
# --- summary ----------------------------------------------------------------
hr
echo "" >&2
echo " Configuration summary:" >&2
echo "" >&2
echo " Mode: $MODE" >&2
case "$MODE" in
local) echo " BirdNET URL: http://$BASE_URL" >&2 ;;
birdweather) echo " ZIP code: $ZIP" >&2 ;;
image) echo " Image URL: $IMAGE_URL" >&2 ;;
esac
if [ "$MAT_W" != "0" ] && [ "$MAT_H" != "0" ]; then
echo " Matte: ${MAT_W}\" × ${MAT_H}\"" >&2
else
echo " Matte: A5 (stock)" >&2
fi
echo " Title: $TITLE / $SUBTITLE" >&2
echo " Rotation: $ROTATE" >&2
if [ "$QUIET_START" != "0" ] || [ "$QUIET_END" != "0" ]; then
echo " Quiet hours: ${QUIET_START}:00 ${QUIET_END}:00" >&2
fi
echo "" >&2
if ! confirm "Proceed with installation?"; then
echo "Cancelled." >&2
exit 0
fi
# --- run install.sh ---------------------------------------------------------
hr
echo "" >&2
echo "Running install.sh $INSTALL_FLAGS ..." >&2
echo "" >&2
./install.sh $INSTALL_FLAGS
# --- write extra config on top of what install.sh generated -----------------
CONFIG="$HOME/.birdframe/config.toml"
# Patch base_url for local mode if not birdnet.local
if [ "$MODE" = "local" ] && [ "$BASE_URL" != "birdnet.local" ]; then
sed -i "s|^base_url = .*|base_url = \"http://$BASE_URL\"|" "$CONFIG"
fi
# Matte opening
if [ "$MAT_W" != "0" ] && [ "$MAT_H" != "0" ]; then
printf '\n# Matte opening size (inches). Overrides the default A5.\nmat_opening_w = %s\nmat_opening_h = %s\n' \
"$MAT_W" "$MAT_H" >> "$CONFIG"
fi
# Titles
if [ "$TITLE" != "Avian Visitors" ] || [ "$SUBTITLE" != "Heard Today" ]; then
sed -i "s|^shoot_title = .*|shoot_title = \"$TITLE\"|" "$CONFIG"
sed -i "s|^shoot_subtitle = .*|shoot_subtitle = \"$SUBTITLE\"|" "$CONFIG"
fi
# Rotation
sed -i "s|^rotate = .*|rotate = $ROTATE|" "$CONFIG"
# Quiet hours
if [ "$QUIET_START" != "0" ] || [ "$QUIET_END" != "0" ]; then
if grep -q "^quiet_start" "$CONFIG" 2>/dev/null; then
sed -i "s|^quiet_start = .*|quiet_start = $QUIET_START|" "$CONFIG"
sed -i "s|^quiet_end = .*|quiet_end = $QUIET_END|" "$CONFIG"
else
printf '\nquiet_start = %s\nquiet_end = %s\n' "$QUIET_START" "$QUIET_END" >> "$CONFIG"
fi
fi
# Basic auth
if [ -n "$BASIC_USER" ]; then
printf '\nbasic_user = "%s"\nbasic_pass = "%s"\n' "$BASIC_USER" "$BASIC_PASS" >> "$CONFIG"
fi
echo "" >&2
echo "Config written to $CONFIG" >&2
echo "Edit it any time: nano $CONFIG" >&2
echo "" >&2