#!/bin/sh # sunset-alarm -- Alert before or after sunset # Displays a persistent alert when current time is within # (sunset minus specified minutes) of midnight. ############################################################################### # Defaults ############################################################################### LATLONG="" SUNSET_TIME="" MINUTES="" SUNSET_EPOCH=0 LAST_DATE="" PROMPT="sunset-alarm" ############################################################################### # Usage ############################################################################### usage() { echo "Usage: sunset-alarm (-l LAT:LONG | -t HH24:MI) (-m MINUTES) [-h]" >&2 exit 1 } ############################################################################### # Parse arguments (handles negative values for -m by using "$@") ############################################################################### while [ $# -gt 0 ]; do case "$1" in -l) LATLONG="$2"; shift 2 ;; -t) SUNSET_TIME="$2"; shift 2 ;; -m) MINUTES="$2"; shift 2 ;; -h) usage ;; --) shift; break ;; *) echo "Error: unknown argument '$1'" >&2; usage ;; esac done # Validate inputs if [ -z "$MINUTES" ]; then echo "Error: -m MINUTES is required." >&2 usage fi if [ -z "$LATLONG" ] && [ -z "$SUNSET_TIME" ]; then echo "Error: must specify either -l LAT:LONG or -t HH:MI." >&2 usage fi ############################################################################### # Internet connectivity check ############################################################################### check_internet() { if curl -s --max-time 5 -o /dev/null https://hyperling,com; then return 0 fi if curl -s --max-time 5 -o /dev/null https://mullvad.net; then return 0 fi return 1 } ############################################################################### # Get sunset from Open Meteo API ############################################################################### fetch_sunset_time() { LAT=$(echo "$LATLONG" | cut -d: -f1) LON=$(echo "$LATLONG" | cut -d: -f2) RESPONSE=$(curl -s "https://api.open-meteo.com/v1/forecast?latitude=${LAT}&longitude=${LON}&timezone=auto&daily=sunset&date=today") if [ -z "$RESPONSE" ]; then return 1 fi # Extract sunset timestamp like "2025-06-15T19:32:00" SUNSET_RAW=$(echo "$RESPONSE" | jq -r '.daily.sunset[0]') if [ -z "$SUNSET_RAW" ]; then return 1 fi # Convert to epoch SUNSET_EPOCH=$(date -d "$(echo "$SUNSET_RAW" | cut -dT -f1) $(echo "$SUNSET_RAW" | cut -dT -f2)" +%s 2>/dev/null) if [ -z "$SUNSET_EPOCH" ]; then return 1 fi return 0 } ############################################################################### # Show alert via fallback chain: fyi -> notify-send -> wall ############################################################################### show_alert() { ALERT=$(date -d @${ALERT_WINDOW_START} +"%H:%M") SUNSET=$(date -d @${SUNSET_EPOCH} +"%H:%M") PLURAL=$(if (( $MINUTES != 1 )); then echo "s"; fi) TITLE="Sunset Alarm ($ALERT)" MESSAGE="It's ${MINUTES} minute${PLURAL} to $SUNSET." persistent="-t 0 --wait" icon="-i weather-clear -u critical" if command -v fyi >/dev/null 2>&1; then fyi $icon $persistent "$TITLE" "$MESSAGE" elif command -v notify-send >/dev/null 2>&1; then notify-send $icon $persistent "$TITLE" "$MESSAGE" elif command -v wall >/dev/null 2>&1; then echo "${TITLE}: ${MESSAGE}" | wall else echo "ERROR: No notification tool found." >&2 echo "Install one of: fyi, notify-send (libnotify-bin), wall (bsd-write)" >&2 return 1 fi } ############################################################################### # Main loop ############################################################################### while true; do TODAY=$(date +%Y-%m-%d) CURRENT_TIME=$(date +%s) # Check if we need to fetch new sunset time for a new day if [ "$LAST_DATE" != "$TODAY" ]; then LAST_DATE="$TODAY" if [ -n "$LATLONG" ]; then # Need internet to get sunset time if ! check_internet; then echo "No internet connection. Retrying in 5 minutes..." >&2 sleep 300 continue fi fetch_sunset_time || { echo "Error fetching sunset time. Retrying in 5 minutes..." >&2 sleep 300 continue } else # User provided sunset time directly, convert to epoch SUNSET_EPOCH=$(date -d "${TODAY} ${SUNSET_TIME}" +%s 2>/dev/null) if [ -z "$SUNSET_EPOCH" ]; then echo "Error: could not parse time '${SUNSET_TIME}'." >&2 sleep 60 continue fi fi fi # Calculate alert window start (sunset minus specified minutes) ALERT_WINDOW_START=$(( SUNSET_EPOCH - (MINUTES * 60) )) # Calculate midnight of the current day MIDNIGHT=$(date -d "${TODAY} +1 day" +%s) # Alert while current time is within the window (from window start until midnight) if [ "$CURRENT_TIME" -ge "$ALERT_WINDOW_START" ] && \ [ "$CURRENT_TIME" -lt "$MIDNIGHT" ]; then show_alert sleep 290 fi # Reset when we've passed midnight so we fetch new time tomorrow if [ "$CURRENT_TIME" -ge "$MIDNIGHT" ]; then LAST_DATE="" fi sleep 10 done