From 66fe1f46ca1bac40194cd240d5c22e9c5cdf781c Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 19 Apr 2026 21:56:33 +0000 Subject: [PATCH 1/4] Fix CAM_NAME bug, set -x noise, wrong Python package in README daily_sunrise_video.sh: derive CAM_NAME from $1 (defaulting to SUNRISE_CAM) instead of an undefined variable; this was causing empty image/output paths. install.sh: pass $SUNRISE_CAM as argument to daily_sunrise_video.sh so it matches the pattern used by every other per-camera script. sunrise2mm.py: use SUNRISE_CAM config key (not the undefined CAM_NAME) to locate the output directory; fixes silent path mismatch on non-default names. 4-seasons.sh: remove stray `set -x` that was flooding systemd logs with shell trace output. README.md: correct Python dependencies from `ephem` to `suntime pytz` to match what sunrise.py actually imports. https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ --- 4-seasons.sh | 1 - README.md | 2 +- daily_sunrise_video.sh | 2 ++ install.sh | 2 +- sunrise2mm.py | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/4-seasons.sh b/4-seasons.sh index 213361c..d917fb1 100755 --- a/4-seasons.sh +++ b/4-seasons.sh @@ -8,7 +8,6 @@ # Run once per day from cron (e.g. 01:00 daily). set -euo pipefail -set -x SCRIPT_DIR="$(dirname "$(realpath "$0")")" source "$SCRIPT_DIR/sky-cam.conf" diff --git a/README.md b/README.md index 2e98066..0e127a4 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Automated sky / timelapse camera scripts that produce: Install Python dependencies: ```bash -pip3 install ephem requests +pip3 install suntime pytz requests ``` --- diff --git a/daily_sunrise_video.sh b/daily_sunrise_video.sh index cba0e87..5e32e14 100644 --- a/daily_sunrise_video.sh +++ b/daily_sunrise_video.sh @@ -17,6 +17,8 @@ SCRIPT_DIR="$(dirname "$(realpath "$0")")" source "$SCRIPT_DIR/sky-cam.conf" export TZ="$TIMEZONE" +CAM_NAME="${1:-$SUNRISE_CAM}" + # ── Date / paths ────────────────────────────────────────────────────────────── current_date=$(date +%Y-%m-%d) output_date=$(date +%Y-%m) diff --git a/install.sh b/install.sh index a687a91..847fb4c 100755 --- a/install.sh +++ b/install.sh @@ -86,7 +86,7 @@ OnSuccess=sky-cam-sunrise-upload.service [Service] Type=oneshot -ExecStart=$SCRIPT_DIR/daily_sunrise_video.sh +ExecStart=$SCRIPT_DIR/daily_sunrise_video.sh $SUNRISE_CAM StandardOutput=journal StandardError=journal EOF diff --git a/sunrise2mm.py b/sunrise2mm.py index 5419fc1..c404eb7 100755 --- a/sunrise2mm.py +++ b/sunrise2mm.py @@ -47,7 +47,7 @@ today_date_str = today.strftime("%Y-%m-%d") # e.g., "2024-11-05" today_month_str = today.strftime("%Y-%m") # e.g., "2024-11" # Set the full folder path for today's video -base_video_folder = os.path.join(config.get('MOVIES_DIR', ''), config.get('CAM_NAME', 'sunrise')) +base_video_folder = os.path.join(config.get('MOVIES_DIR', ''), config.get('SUNRISE_CAM', 'sunrise')) output_dir = os.path.join(base_video_folder, today_month_str, "sunrise-only") # Check if the directory exists From ba1eb704364b95c0fe0a6a56ee398a585ee399a3 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 19 Apr 2026 22:45:26 +0000 Subject: [PATCH 2/4] bootstrap.sh: fix post-install instructions (remove CAM_NAME, add pip step) CAM_NAME is no longer a config key; replace with CAMERAS + SUNRISE_CAM. Add pip3 install step for suntime/pytz/requests as step 1. https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ --- bootstrap.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/bootstrap.sh b/bootstrap.sh index 1f63b7a..0c015b9 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -20,12 +20,15 @@ mv "sky-cam-$BRANCH" "$TARGET" echo "" echo "Done. Next steps:" -echo " 1. Edit $TARGET/sky-cam.conf" +echo " 1. pip3 install suntime pytz requests" +echo "" +echo " 2. Edit $TARGET/sky-cam.conf" echo " — SCRIPT_DIR full path to the directory you'll run scripts from" echo " — BASE_DIR where your camera images live" echo " — MUSIC_DIR path to your Vivaldi Four Seasons audio files" -echo " — CAM_NAME camera subfolder name (e.g. sunrise)" +echo " — CAMERAS list of camera names, e.g. (sunrise)" +echo " — SUNRISE_CAM which camera faces east (gets the sunrise video job)" echo " — LATITUDE / LONGITUDE / TIMEZONE your location" echo " — Mattermost credentials (mattermost_url, access_token, channel_id)" echo "" -echo " 2. cd $TARGET && ./install.sh" +echo " 3. cd $TARGET && ./install.sh" From a1b9ffa8ee9e8033388b6864f5a27c22fcade6f8 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 19 Apr 2026 23:08:01 +0000 Subject: [PATCH 3/4] Add .env credentials file support; gitignore .env and sunrise-sounds/ sky-cam.conf now sources .env from the same directory at the bottom of the file, so RTSP URLs, Mattermost tokens, and notification credentials can live outside version control. Plaintext credential placeholders removed from conf. install.sh generates .env.example on every run with the correct CAM_RTSP_* variable names derived from the CAMERAS list, so users always know exactly what to put in .env for their specific camera setup. .gitignore: add .env and sunrise-sounds/ (downloaded audio library). https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ --- .gitignore | 2 ++ install.sh | 23 +++++++++++++++++++++++ sky-cam.conf | 22 +++++++++++++++++----- 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index c18dd8d..00d2826 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ __pycache__/ +.env +sunrise-sounds/ diff --git a/install.sh b/install.sh index 847fb4c..b343297 100755 --- a/install.sh +++ b/install.sh @@ -186,6 +186,29 @@ for cam in "${CAMERAS[@]}"; do fi done +# ── Generate .env.example ──────────────────────────────────────────────────── +env_example="$HERE/.env.example" +{ + echo "# sky-cam credentials — copy to .env and fill in real values." + echo "# .env is gitignored and never checked in." + echo "" + echo "# RTSP stream URLs — one line per camera (variable name = CAM_RTSP_)" + 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 (uncomment whichever you use)" + echo "#NTFY_URL=https://ntfy.sh/your-topic" + echo "#EMAIL_TO=you@example.com" + 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)" + # ── Reload and enable ───────────────────────────────────────────────────────── echo "" $SC daemon-reload diff --git a/sky-cam.conf b/sky-cam.conf index 1c4e8a3..fe6a7b6 100644 --- a/sky-cam.conf +++ b/sky-cam.conf @@ -245,8 +245,7 @@ MONTAGE_ATTR_FADE=1 # attribution fade-in and fade-out length (seconds) # Find the URL in your camera's web UI (usually under Network → Video → RTSP) # or check your camera's manual. # -#CAM_RTSP_sunrise=rtsp://admin:password@192.168.1.100:554/stream1 -#CAM_RTSP_north=rtsp://admin:password@192.168.1.101:554/stream1 +# CAM_RTSP_ goes in .env (see bottom of this file), not here. # Seconds between captured frames (applies to all cameras). # 10 = one frame every 10 s → 8640 frames/day → good timelapse density. @@ -263,9 +262,7 @@ AUDIO_PRE_BUFFER_MIN=2 # extra minutes to start before the capture window CAPTURE_AUDIO_BITRATE=96k # bitrate for the sunrise audio recording # ── Mattermost — daily sunrise upload ───────────────────────────────────────── -mattermost_url=https://your-mattermost-server.example.com -access_token=your-access-token-here -channel_id=your-daily-upload-channel-id-here +# mattermost_url, access_token, channel_id go in .env (see bottom of this file). # ── Notifications — montage / year-end complete + any job failure ───────────── # Notifications fire when a movement montage or the year video finishes, @@ -288,3 +285,18 @@ channel_id=your-daily-upload-channel-id-here # Can be the same daily channel or a separate admin/private channel. #MM_NOTIFY_ENABLED=true #MM_NOTIFY_CHANNEL_ID=your-notify-channel-id-here + +# ── Credentials (.env override) ─────────────────────────────────────────────── +# Sensitive values (RTSP URLs with passwords, API tokens) can be kept out of +# this file — and out of git — by placing them in a .env file in the same +# directory. install.sh generates .env.example showing exactly which variables +# to define for your camera list. +# +# To use: +# cp .env.example .env +# $EDITOR .env ← fill in real values +# (never commit .env) +# +_conf_dir="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" +[ -f "$_conf_dir/.env" ] && source "$_conf_dir/.env" +unset _conf_dir From a1cf1304e46b1c0953291faa8e698e148d000c4e Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 19 Apr 2026 23:37:10 +0000 Subject: [PATCH 4/4] Fix first-run blockers: MOVIES_DIR path, CAM_NAME, .env for Python, eval safety daily_sunrise_video.sh: use \$MOVIES_DIR instead of \$BASE_DIR/movies so the override in sky-cam.conf is respected when videos live on a separate drive. capture.sh: default camera name falls back to \$SUNRISE_CAM (not the undefined \$CAM_NAME) when no argument is passed. sunrise2mm.py: also loads .env after sky-cam.conf so Mattermost credentials moved to .env are actually visible to the upload script. sunrise.py: raise a clear error message when LATITUDE/LONGITUDE/TIMEZONE are missing from sky-cam.conf instead of an opaque KeyError. 4-seasons.sh, montage-mvt.sh: capture season_info.py output before eval so a Python failure exits cleanly with a diagnostic message rather than silently continuing with undefined variables. bootstrap.sh: add system package install step (ffmpeg bc fonts-dejavu) and show the .env setup step after install.sh generates .env.example. README.md: correct Python package names and add fonts-dejavu dependency. https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ --- 4-seasons.sh | 6 +++++- README.md | 3 ++- bootstrap.sh | 8 ++++++-- capture.sh | 2 +- daily_sunrise_video.sh | 2 +- montage-mvt.sh | 6 +++++- sunrise.py | 4 ++++ sunrise2mm.py | 5 ++++- 8 files changed, 28 insertions(+), 8 deletions(-) diff --git a/4-seasons.sh b/4-seasons.sh index d917fb1..03c825e 100755 --- a/4-seasons.sh +++ b/4-seasons.sh @@ -26,7 +26,11 @@ yesterday=$(date --date="yesterday" +%Y-%m-%d) echo "Processing: $yesterday" # ── Season / movement info from astronomical calculation ────────────────────── -eval "$(python3 "$SCRIPT_DIR/season_info.py" "$yesterday")" +_season_info="$(python3 "$SCRIPT_DIR/season_info.py" "$yesterday")" || { + echo "Error: season_info.py failed — check Python dependencies (suntime pytz)" + exit 1 +} +eval "$_season_info" # Provides: SEASON MVT_NUM DAY_OF_MVT DAYS_IN_MVT MVT_START MVT_END # IS_LAST_DAY ASTRO_YEAR echo "Season=$SEASON Mvt=$MVT_NUM Year=$ASTRO_YEAR Day=$DAY_OF_MVT/$DAYS_IN_MVT LastDay=$IS_LAST_DAY" diff --git a/README.md b/README.md index 0e127a4..006c5b1 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,8 @@ Automated sky / timelapse camera scripts that produce: | Dependency | Notes | |---|---| | `ffmpeg` + `ffprobe` | Encoding, capture, audio recording, duration probing | -| `python3` | `ephem` package for sunrise calculation, `requests` for Mattermost upload | +| `python3` | `suntime pytz` for sunrise calculation, `requests` for Mattermost upload — `pip3 install suntime pytz requests` | +| `fonts-dejavu` | DejaVu fonts for text overlays — `sudo apt install fonts-dejavu` | | `bc` | Shell arithmetic (floating-point speed factors) | | `fontconfig` (`fc-match`) | Font detection for overlays — optional, falls back to hardcoded paths | | IP camera with RTSP stream | `capture.sh` pulls frames directly — no NVR software needed | diff --git a/bootstrap.sh b/bootstrap.sh index 0c015b9..8bb45ea 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -20,7 +20,9 @@ mv "sky-cam-$BRANCH" "$TARGET" echo "" echo "Done. Next steps:" -echo " 1. pip3 install suntime pytz requests" +echo " 1. Install system packages (if not already present):" +echo " sudo apt install ffmpeg bc fonts-dejavu" +echo " pip3 install suntime pytz requests" echo "" echo " 2. Edit $TARGET/sky-cam.conf" echo " — SCRIPT_DIR full path to the directory you'll run scripts from" @@ -29,6 +31,8 @@ echo " — MUSIC_DIR path to your Vivaldi Four Seasons audio files" echo " — CAMERAS list of camera names, e.g. (sunrise)" echo " — SUNRISE_CAM which camera faces east (gets the sunrise video job)" echo " — LATITUDE / LONGITUDE / TIMEZONE your location" -echo " — Mattermost credentials (mattermost_url, access_token, channel_id)" echo "" echo " 3. cd $TARGET && ./install.sh" +echo " This generates .env.example — copy it to .env and add credentials:" +echo " cp $TARGET/.env.example $TARGET/.env" +echo " \$EDITOR $TARGET/.env" diff --git a/capture.sh b/capture.sh index c447971..a85e007 100755 --- a/capture.sh +++ b/capture.sh @@ -17,7 +17,7 @@ SCRIPT_DIR="$(dirname "$(realpath "$0")")" source "$SCRIPT_DIR/sky-cam.conf" export TZ="$TIMEZONE" -CAM="${1:-$CAM_NAME}" +CAM="${1:-$SUNRISE_CAM}" rtsp_var="CAM_RTSP_${CAM}" RTSP_URL="${!rtsp_var:-}" diff --git a/daily_sunrise_video.sh b/daily_sunrise_video.sh index 5e32e14..a259fc1 100644 --- a/daily_sunrise_video.sh +++ b/daily_sunrise_video.sh @@ -24,7 +24,7 @@ current_date=$(date +%Y-%m-%d) output_date=$(date +%Y-%m) image_dir="$BASE_DIR/$CAM_NAME/$current_date" -output_dir="$BASE_DIR/movies/$CAM_NAME/$output_date/sunrise-only" +output_dir="$MOVIES_DIR/$CAM_NAME/$output_date/sunrise-only" mkdir -p "$output_dir" # ── Sunrise time ────────────────────────────────────────────────────────────── diff --git a/montage-mvt.sh b/montage-mvt.sh index d55b10c..9fc08a1 100755 --- a/montage-mvt.sh +++ b/montage-mvt.sh @@ -39,7 +39,11 @@ ATTR_FADE="$MONTAGE_ATTR_FADE" # ── Season / movement info ──────────────────────────────────────────────────── DATE_ARG="${1:-}" -eval "$(python3 "$SCRIPT_DIR/season_info.py" ${DATE_ARG:+"$DATE_ARG"})" +_season_info="$(python3 "$SCRIPT_DIR/season_info.py" ${DATE_ARG:+"$DATE_ARG"})" || { + echo "Error: season_info.py failed — check Python dependencies (suntime pytz)" + exit 1 +} +eval "$_season_info" # Provides: SEASON MVT_NUM DAY_OF_MVT DAYS_IN_MVT MVT_START MVT_END # IS_LAST_DAY ASTRO_YEAR echo "Season=$SEASON Mvt=$MVT_NUM Year=$ASTRO_YEAR (ref: ${DATE_ARG:-today})" diff --git a/sunrise.py b/sunrise.py index 2646b44..5d2fbc1 100644 --- a/sunrise.py +++ b/sunrise.py @@ -18,6 +18,10 @@ def _read_conf(path): conf = _read_conf(_here / 'sky-cam.conf') +for _key in ('LATITUDE', 'LONGITUDE', 'TIMEZONE'): + if _key not in conf: + raise SystemExit(f"sunrise.py: {_key} not set in sky-cam.conf") + latitude = float(conf['LATITUDE']) longitude = float(conf['LONGITUDE']) tz = pytz.timezone(conf['TIMEZONE']) diff --git a/sunrise2mm.py b/sunrise2mm.py index c404eb7..af2357e 100755 --- a/sunrise2mm.py +++ b/sunrise2mm.py @@ -28,8 +28,11 @@ def read_config(config_file_path): exit(1) return config -# Read configuration from the file +# Read configuration — sky-cam.conf first, then .env overrides (credentials) config = read_config(config_file_path) +_env_path = str(_here / '.env') +if os.path.isfile(_env_path): + config.update(read_config(_env_path)) # Get the values from the configuration file mattermost_url = config.get('mattermost_url')