auto-update-daily-20260202
  1#!/usr/bin/env bash
  2# get-weather - Get current weather conditions
  3# Copyright (C) 2025 Vincent Demeester
  4# Part of Claude Code Journal skill
  5
  6set -euo pipefail
  7
  8# Configuration
  9CACHE_FILE="${XDG_CACHE_HOME:-$HOME/.cache}/journal-weather"
 10CACHE_TIMEOUT=1800  # 30 minutes in seconds
 11
 12# Colors for output
 13RED='\033[0;31m'
 14YELLOW='\033[1;33m'
 15NC='\033[0m'
 16
 17error() {
 18    echo -e "${RED}Error: $*${NC}" >&2
 19    exit 1
 20}
 21
 22debug() {
 23    if [[ "${DEBUG:-0}" == "1" ]]; then
 24        echo -e "${YELLOW}Debug: $*${NC}" >&2
 25    fi
 26}
 27
 28usage() {
 29    cat <<EOF
 30get-weather - Get current weather conditions
 31
 32USAGE:
 33    get-weather [location] [options]
 34
 35ARGUMENTS:
 36    location            Optional location (city name, coordinates, airport code)
 37                       If not provided, uses current location from IP
 38
 39OPTIONS:
 40    --json              Output as JSON with all fields
 41    --temperature       Output temperature only (e.g., "15,2°C")
 42    --condition         Output condition only (e.g., "Partly cloudy")
 43    --symbol            Output weather symbol only (e.g., "cloud.sun")
 44    --all               Output formatted: temp, condition, symbol (default)
 45    --no-cache          Don't use cached weather data
 46    --help, -h          Show this help
 47
 48OUTPUT FORMATS:
 49    Default:    15,2°C Partly cloudy (cloud.sun)
 50    --json:     {"temperature":"15,2°C","condition":"Partly cloudy","symbol":"cloud.sun"}
 51    --temperature: 15,2°C
 52    --condition:   Partly cloudy
 53    --symbol:      cloud.sun
 54
 55WEATHER SYMBOLS (iOS SF Symbols compatible):
 56    sun.max              - Clear/Sunny
 57    cloud.sun            - Partly cloudy
 58    cloud                - Cloudy/Overcast
 59    cloud.rain           - Rain
 60    cloud.drizzle        - Light rain/Drizzle
 61    cloud.heavyrain      - Heavy rain
 62    cloud.snow           - Snow
 63    cloud.sleet          - Sleet
 64    cloud.fog            - Fog/Mist
 65    smoke                - Haze/Smoke
 66    wind                 - Windy
 67    cloud.bolt           - Thunderstorm
 68    moon.stars           - Clear night
 69    cloud.moon           - Partly cloudy night
 70    cloud.moon.rain      - Rainy night
 71
 72EXAMPLES:
 73    # Get weather for current location
 74    get-weather
 75
 76    # Get weather for specific city
 77    get-weather Paris
 78
 79    # Get just temperature for journelly-manager
 80    get-weather --temperature
 81
 82    # Get all fields as JSON
 83    get-weather --json
 84
 85    # Get weather for coordinates
 86    get-weather "48.8566,2.3522"
 87
 88    # Force refresh (ignore cache)
 89    get-weather --no-cache
 90
 91NOTES:
 92    - Uses wttr.in weather service (no API key required)
 93    - Weather is cached for 30 minutes to reduce API calls
 94    - Automatic location detection via IP if no location specified
 95
 96VERSION:
 97    1.0.0
 98
 99AUTHOR:
100    Vincent Demeester <vincent@demeester.fr>
101EOF
102}
103
104# Map wttr.in weather codes to iOS SF Symbol names
105map_weather_symbol() {
106    local desc="$1"
107    local is_night="${2:-false}"
108
109    desc=$(echo "$desc" | tr '[:upper:]' '[:lower:]')
110
111    # Night conditions
112    if [[ "$is_night" == "true" ]]; then
113        case "$desc" in
114            *partly*cloudy*|*partly*clear*)
115                echo "cloud.moon"
116                ;;
117            *clear*|*sunny*)
118                echo "moon.stars"
119                ;;
120            *rain*|*drizzle*|*shower*)
121                echo "cloud.moon.rain"
122                ;;
123            *)
124                echo "cloud.moon"
125                ;;
126        esac
127        return
128    fi
129
130    # Day conditions
131    case "$desc" in
132        *partly*cloudy*|*partly*clear*)
133            echo "cloud.sun"
134            ;;
135        *clear*|*sunny*)
136            echo "sun.max"
137            ;;
138        *cloudy*|*overcast*)
139            echo "cloud"
140            ;;
141        *heavy*rain*)
142            echo "cloud.heavyrain"
143            ;;
144        *drizzle*|*light*rain*)
145            echo "cloud.drizzle"
146            ;;
147        *rain*|*shower*)
148            echo "cloud.rain"
149            ;;
150        *snow*)
151            echo "cloud.snow"
152            ;;
153        *sleet*)
154            echo "cloud.sleet"
155            ;;
156        *fog*|*mist*)
157            echo "cloud.fog"
158            ;;
159        *haze*|*smoke*)
160            echo "smoke"
161            ;;
162        *wind*)
163            echo "wind"
164            ;;
165        *thunder*|*storm*)
166            echo "cloud.bolt"
167            ;;
168        *)
169            echo "cloud"
170            ;;
171    esac
172}
173
174# Check if it's currently night time (simple heuristic based on hour)
175is_night() {
176    local hour
177    hour=$(date +%H)
178    # Consider 20:00-06:00 as night
179    [[ $hour -ge 20 || $hour -lt 6 ]]
180}
181
182# Check if cache is valid
183is_cache_valid() {
184    local location="${1:-auto}"
185    local cache_key="${CACHE_FILE}_${location// /_}"
186
187    [[ -f "$cache_key" ]] || return 1
188
189    local cache_age
190    cache_age=$(($(date +%s) - $(stat -c %Y "$cache_key" 2>/dev/null || echo 0)))
191
192    [[ $cache_age -lt $CACHE_TIMEOUT ]]
193}
194
195# Get weather from cache
196get_from_cache() {
197    local location="${1:-auto}"
198    local cache_key="${CACHE_FILE}_${location// /_}"
199
200    if [[ -f "$cache_key" ]]; then
201        cat "$cache_key"
202        return 0
203    fi
204    return 1
205}
206
207# Get weather from wttr.in
208get_from_api() {
209    local location="${1:-}"
210
211    debug "Fetching weather from wttr.in for: ${location:-current location}"
212
213    local url="https://wttr.in/${location}?format=j1"
214    local response
215
216    response=$(curl -s --max-time 10 "$url" 2>/dev/null) || {
217        error "Failed to fetch weather from wttr.in"
218    }
219
220    # Validate response
221    if ! echo "$response" | jq -e '.current_condition' >/dev/null 2>&1; then
222        error "Invalid response from wttr.in"
223    fi
224
225    echo "$response"
226}
227
228# Parse and format output
229format_output() {
230    local data="$1"
231    local format="${2:-all}"
232
233    local temp_c
234    local condition
235    local is_night_time
236
237    # Extract data
238    temp_c=$(echo "$data" | jq -r '.current_condition[0].temp_C // ""')
239    condition=$(echo "$data" | jq -r '.current_condition[0].weatherDesc[0].value // ""')
240
241    if [[ -z "$temp_c" || -z "$condition" ]]; then
242        error "No weather data available"
243    fi
244
245    # Format temperature (use comma as decimal separator to match Journelly format)
246    local temperature="${temp_c}°C"
247
248    # Determine if it's night
249    if is_night; then
250        is_night_time="true"
251    else
252        is_night_time="false"
253    fi
254
255    # Map to symbol
256    local symbol
257    symbol=$(map_weather_symbol "$condition" "$is_night_time")
258
259    case "$format" in
260        json)
261            jq -n \
262                --arg temp "$temperature" \
263                --arg cond "$condition" \
264                --arg sym "$symbol" \
265                '{temperature: $temp, condition: $cond, symbol: $sym}'
266            ;;
267        temperature)
268            echo "$temperature"
269            ;;
270        condition)
271            echo "$condition"
272            ;;
273        symbol)
274            echo "$symbol"
275            ;;
276        all)
277            echo "$temperature $condition ($symbol)"
278            ;;
279        journelly)
280            # Format for journelly-manager command line
281            echo "--temperature=\"$temperature\" --condition=\"$condition\" --symbol=\"$symbol\""
282            ;;
283        *)
284            error "Unknown format: $format"
285            ;;
286    esac
287}
288
289# Main function
290main() {
291    local use_cache=true
292    local format="all"
293    local location=""
294
295    # Parse arguments
296    while [[ $# -gt 0 ]]; do
297        case "$1" in
298            --json)
299                format="json"
300                ;;
301            --temperature)
302                format="temperature"
303                ;;
304            --condition)
305                format="condition"
306                ;;
307            --symbol)
308                format="symbol"
309                ;;
310            --all)
311                format="all"
312                ;;
313            --journelly)
314                format="journelly"
315                ;;
316            --no-cache)
317                use_cache=false
318                ;;
319            --help|-h)
320                usage
321                exit 0
322                ;;
323            -*)
324                error "Unknown option: $1. Use --help for usage."
325                ;;
326            *)
327                location="$1"
328                ;;
329        esac
330        shift
331    done
332
333    local data=""
334    local cache_key="${location:-auto}"
335
336    # Try cache first
337    if [[ "$use_cache" == "true" ]] && is_cache_valid "$cache_key"; then
338        debug "Using cached weather for $cache_key"
339        data=$(get_from_cache "$cache_key")
340    else
341        # Fetch from API
342        data=$(get_from_api "$location")
343
344        # Cache the result
345        mkdir -p "$(dirname "$CACHE_FILE")"
346        local cache_file="${CACHE_FILE}_${cache_key// /_}"
347        echo "$data" > "$cache_file"
348        debug "Weather cached to $cache_file"
349    fi
350
351    # Format and output
352    format_output "$data" "$format"
353}
354
355main "$@"