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>
This commit is contained in:
Executable
+240
@@ -0,0 +1,240 @@
|
||||
#!/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
|
||||
Reference in New Issue
Block a user