#!/bin/bash # install.sh — generate and install sky-cam systemd units from sky-cam.conf. # # Usage: # ./install.sh # installs to ~/.config/systemd/user (no root needed) # ./install.sh --system # installs to /etc/systemd/system (needs sudo) # # Run this once after editing sky-cam.conf, and again whenever sky-cam.conf # changes (e.g. SCRIPT_DIR moves, cameras added, schedules changed). # No other file needs editing. set -euo pipefail HERE="$(dirname "$(realpath "$0")")" source "$HERE/sky-cam.conf" # ── Install target ──────────────────────────────────────────────────────────── SYSTEM_MODE=false [ "${1:-}" = "--system" ] && SYSTEM_MODE=true if $SYSTEM_MODE; then UNIT_DIR="/etc/systemd/system" SC="sudo systemctl" else UNIT_DIR="$HOME/.config/systemd/user" SC="systemctl --user" fi mkdir -p "$UNIT_DIR" echo "Installing systemd units to $UNIT_DIR ..." # ── Ensure all scripts are executable ──────────────────────────────────────── chmod +x "$HERE"/*.sh "$HERE"/*.py 2>/dev/null || true # ── Pre-create data directories for all cameras ─────────────────────────────── echo "Creating data directories..." for cam in "${CAMERAS[@]}"; do mkdir -p "$BASE_DIR/$cam" mkdir -p "$MOVIES_DIR/$cam" mkdir -p "$AUDIO_DIR/$cam" echo " $BASE_DIR/$cam" echo " $MOVIES_DIR/$cam" echo " $AUDIO_DIR/$cam" done mkdir -p "$BASE_DIR/$SUNRISE_CAM" mkdir -p "$AUDIO_DIR/sunrise/$SUNRISE_CAM" echo "" # ── Helper: write a oneshot service unit ───────────────────────────────────── write_service() { local unit="$1" description="$2" script="$3" extra="${4:-}" cat > "$UNIT_DIR/${unit}.service" < "$UNIT_DIR/${unit}.timer" < "$UNIT_DIR/sky-cam-notify-failure@.service" < "$UNIT_DIR/sky-cam-sunrise.service" < "$UNIT_DIR/sky-cam-sunrise-upload.service" < is set; Type=simple (long-running, not oneshot). capture_services=() for cam in "${CAMERAS[@]}"; do rtsp_var="CAM_RTSP_${cam}" if [ -n "${!rtsp_var:-}" ]; then cat > "$UNIT_DIR/sky-cam-capture-${cam}.service" < "$UNIT_DIR/sky-cam-watchdog-${cam}.service" < "$UNIT_DIR/sky-cam-ambient-${cam}.service" < from sky-cam.conf. # Script receives the camera name as $1 so it knows which camera to process. for cam in "${CAMERAS[@]}"; do sched_seasons_var="SCHEDULE_SEASONS_${cam}" if [ -n "${!sched_seasons_var:-}" ]; then write_service "sky-cam-seasons-${cam}" \ "${cam}: Four Seasons daily clip" \ "4-seasons.sh ${cam}" \ $'After=network-online.target\nWants=network-online.target\n' write_timer "sky-cam-seasons-${cam}" "${cam}: Four Seasons daily clip" \ "${!sched_seasons_var}" timers+=("sky-cam-seasons-${cam}") else echo " WARNING: SCHEDULE_SEASONS_${cam} not set — skipping seasons timer for ${cam}" fi done # ── Generate .env.example ──────────────────────────────────────────────────── env_example="$HERE/.env.example" { echo "# sky-cam runtime config — copy to .env and fill in real values." echo "# .env is gitignored and never checked in." echo "# Everything here overrides the defaults in sky-cam.conf." echo "" echo "# ── Location ────────────────────────────────────────────────────────────────" echo "# Used by sunrise.py to calculate today's sunrise time." echo "# Right-click your location in Google Maps to get lat/long." echo "# TIMEZONE: use the TZ identifier column from" echo "# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones" echo "LATITUDE=0.0000" echo "LONGITUDE=0.0000" echo "TIMEZONE=America/New_York" echo "" echo "# ── Storage paths ───────────────────────────────────────────────────────────" echo "# Override any of these if your images, videos, or music live on a" echo "# different drive. Leave commented to use the defaults next to the scripts." echo "#BASE_DIR=/path/to/images # default: ~/sky-cam/data" echo "#MOVIES_DIR=/path/to/videos # default: \$BASE_DIR/movies" echo "#AUDIO_DIR=/path/to/audio # default: \$BASE_DIR/audio" echo "#MUSIC_DIR=/path/to/music/4Seasons # default: ~/sky-cam/music" echo "" echo "# ── RTSP stream URLs ────────────────────────────────────────────────────────" echo "# One line per camera. Use single quotes to prevent shell interpretation." echo "# URL-encode special characters in the password:" echo "# !=%21 @=%40 #=%23 \$=%24 %=%25 ^=%5E &=%26 *=%2A :=%3A /=%2F" echo "# Example: password my@p\$ss:word! → my%40p%24ss%3Aword%21" echo "" for cam in "${CAMERAS[@]}"; do echo "CAM_RTSP_${cam}='rtsp://admin:PASSWORD@192.168.1.XXX:554/stream1'" done echo "" echo "# ── Mattermost upload ───────────────────────────────────────────────────────" echo "mattermost_url=https://your-mattermost-server.example.com" echo "access_token=your-access-token-here" echo "channel_id=your-daily-upload-channel-id-here" echo "" echo "# ── Notifications ───────────────────────────────────────────────────────────" echo "# Set the private values here, then flip the matching *_ENABLED toggle" echo "# to true in sky-cam.conf." echo "#NTFY_URL=https://ntfy.sh/your-topic" echo "#EMAIL_TO=you@example.com" echo "#EMAIL_FROM=skycam@localhost" echo "#MM_NOTIFY_CHANNEL_ID=your-notify-channel-id-here" } > "$env_example" echo " wrote .env.example (cp .env.example .env then fill in real values)" # ── User-session prerequisites (non-system installs only) ──────────────────── if ! $SYSTEM_MODE; then # Make systemctl --user reachable when run outside a login session export XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}" # Start user services at boot even without an interactive login loginctl enable-linger "$USER" \ && echo " loginctl enable-linger: ok" \ || echo " WARNING: loginctl enable-linger failed (may need to run manually)" fi # ── Reload and enable ───────────────────────────────────────────────────────── echo "" $SC daemon-reload for svc in "${capture_services[@]}"; do if $SC enable "${svc}.service" && $SC restart "${svc}.service"; then echo " enabled + started ${svc}.service" else echo " WARNING: could not enable/start ${svc}.service" fi done for timer in "${timers[@]}"; do $SC enable --now "${timer}.timer" \ && echo " enabled + started ${timer}.timer" \ || echo " WARNING: could not enable ${timer}.timer" done echo "" echo "Done. Check status with:" if $SYSTEM_MODE; then echo " sudo systemctl list-timers 'sky-cam-*'" echo " sudo journalctl -u sky-cam-seasons-${CAMERAS[0]}.service -f" else echo " systemctl --user list-timers 'sky-cam-*'" echo " journalctl --user -u sky-cam-seasons-${CAMERAS[0]}.service -f" fi