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 "$@"