Compare commits

..
15 Commits
2 changed files with 275 additions and 2 deletions

No files matched your search

+104 -2
View File
@@ -1,3 +1,105 @@
# template-mit
# Sunset Alarm (CLI)
Template with MIT license, protected `main` branch, and available `dev` branch.
Simple command line shell script which can be run as a user daemon for alerts.
## Description
Sunset Alarm allows a user to receive an alert at a specified amount of minutes
before sunset. This may be helpful for starting any practices such as preparing
for sungazing, or as a reminder to go and find a campsite before it gets dark.
## Usage
`()` sections are required.
`[]` sections are optional.
`|` separates multiple options in a group.
```console
sunset-timer (-l LAT:LONG | -t HH24:MI) (-m MINUTES) [-h]
```
## Example
Set an alarm 90 minutes before for your lat/long experiences sunset:
```shell
sunset-alarm -l 32.123:-111.111 -m 90
```
Set an alarm 5 minutes after a manually determined sunset time:
```shell
sunset-alarm -t 18:00 -m -5
```
## Install
Recommended installation location is under `/usr/local/*`.
```console
# git clone http://git.hyperling.com/me/sunset-alarm-cli /usr/local/src/sunset_alarm_cli
# ln -s /usr/local/src/sunset_alarm_cli/sunset-alarm /usr/local/bin/
```
### Daemonize
**runit**
Create the run file:
```console
# vi /etc/sv/sunset-alarm/run:
```
```shell
#!/bin/sh -e
exec 2>&1
chpst -u user /usr/local/bin/sunset-alarm -l 33:-112 -m 15
```
Enable the service:
```console
# ln -s /etc/sv/sunset-alarm /var/services/
```
### i3
Simply add an exec to the end of your config file.
```shell
echo "exec --no-startup-id /usr/local/bin/sunset-alarm -l 33:-112 -m 15" >> ~/.config/i3/config
```
## Parameters
### Required
-m : Accepts a minute value which is used to check if we have approached the
alert period before sunset. Can be positive, negative, or 0.
### Optional
-l : Accepts a lat:long value which is used to check Open Meteo for the
location's sunset time. This is required if -t is not passed.
-t : Accepts a time value for sunset time. If this is passed, it bypasses the
Open Meteo check and acts as a hardcoded time. The minutes parameter is still
required to know when to alert prior to this time.
## Design
App displays a persistent alert via `fyi` with fallbacks of `notify-send`,
`wall`, and others. Appears once when current time is between (sunset - minutes)
and midnight. Even if the system is powered on after the alert time has passed,
the program continues displaying the alert repeatedly until the day has passed.
App requires an internet connection. If no connection is found, it sleeps for 5
minutes and then tries again to retrieve the sunset time. Once the sunset time
has been retrieved for the day, it stops checking Open Meteo until the next day.
App is short, sweet, concise, and succinct without using any odd syntax or line
combining to achieve this. Does not pull in any extraneous libraries, only uses
utilities which are already installed on the system. Program is coded to use a
range of utilities in case the primary choices are not available. If no choices
are installed, displays an ERROR message with a list of software to download.
Executable
+171
View File
@@ -0,0 +1,171 @@
#!/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" | grep -o '"sunset":"[^"]*"' | head -1 | sed 's/.*://"//;s/"$//')
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() {
TITLE="Sunset Alarm"
MESSAGE="Sunset is at $(date -d @${SUNSET_EPOCH} +"%H:%M")."
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 5 # Re-alert every 5 minutes while in alert window
else
sleep 60
fi
# Reset when we've passed midnight so we fetch new time tomorrow
if [ "$CURRENT_TIME" -ge "$MIDNIGHT" ]; then
LAST_DATE=""
fi
done