1#!/bin/sh
2
3#
4# AnsiWeather 1.18.0
5# Copyright (c) 2013-2021, Frederic Cambus
6# https://github.com/fcambus/ansiweather
7#
8# Created: 2013-08-29
9# Last Updated: 2021-09-14
10#
11# AnsiWeather is released under the BSD 2-Clause license
12# See LICENSE file for details
13#
14# SPDX-License-Identifier: BSD-2-Clause
15#
16
17###[ Configuration options ]###################################################
18
19LC_ALL=C; export LC_ALL
20
21if [ -n "$ANSIWEATHERRC" ] ; then
22	config_file="$ANSIWEATHERRC"
23elif [ -s "$XDG_CONFIG_HOME"/ansiweather/config ] ; then
24	config_file="$XDG_CONFIG_HOME"/ansiweather/config
25elif [ -s "$HOME"/.config/ansiweather/config ] ; then
26	config_file="$HOME"/.config/ansiweather/config
27else
28	config_file=~/.ansiweatherrc
29fi
30
31get_config() {
32	ret=""
33	if [ -f "$config_file" ]
34	then
35		ret=$(grep "^$1:" "$config_file" | cut -d: -f2-)
36	fi
37
38	if [ "X$ret" = "X" ]
39	then
40		return 1
41	else
42		echo "$ret"
43	fi
44}
45
46fetch_cmd=$(get_config "fetch_cmd" || echo "curl -sf")
47
48
49
50###[ Parse the command line ]##################################################
51
52# Get config options from command line flags
53while getopts l:u:f:FH:a:s:k:i:w:h:p:d:v option
54do
55	case "${option}"
56	in
57		l) location=${OPTARG};;
58		u) units=${OPTARG};;
59		f) forecast=${OPTARG};;
60		F) forecast="5";;
61		H) show_feels_like=${OPTARG};;
62		a) ansi=${OPTARG};;
63		s) symbols=${OPTARG};;
64		k) api_key=${OPTARG};;
65		i) show_uvi=${OPTARG};;
66		w) show_wind=${OPTARG};;
67		h) show_humidity=${OPTARG};;
68		p) show_pressure=${OPTARG};;
69		d) show_daylight=${OPTARG};;
70		v) echo "AnsiWeather 1.18.0" && exit 0;;
71		\?) exit 64;; # EX_USAGE
72	esac
73done
74
75
76
77###[ Check if bc and jq are installed ]########################################
78
79jqpath=$(command -v jq)
80if [ "$jqpath" = "" ]
81then
82	echo "ERROR: Cannot find jq binary"
83	exit 69 # EX_UNAVAILABLE
84fi
85
86bcpath=$(command -v bc)
87if [ "$bcpath" = "" ]
88then
89	echo "ERROR: Cannot find bc binary"
90	exit 69 # EX_UNAVAILABLE
91fi
92
93
94
95###[ Set options that are not set from command line ]##########################
96
97# OpenWeatherMap API key
98[ -z "$api_key" ] && api_key=$(get_config "api_key" || echo "85a4e3c55b73909f42c6a23ec35b7147")
99
100# Location: example "Rzeszow,PL"
101[ -z "$location" ] && location=$(get_config "location" || echo "Rzeszow,PL")
102
103# System of Units: "metric" or "imperial"
104[ -z "$units" ] && units=$(get_config "units" || echo "metric")
105
106# Show forecast: How many days, example "5". "0" is standard output
107[ -z "$forecast" ] && forecast=$(get_config "forecast" || echo 0)
108
109# Display ANSI colors: "true" or "false"
110[ -z "$ansi" ] && ansi=$(get_config "ansi" || echo true)
111
112# Display symbols: "true" or "false" (requires a Unicode capable display)
113[ -z "$symbols" ] && symbols=$(get_config "symbols" || echo false)
114
115# Show feels-like: "true" or "false"
116[ -z "$show_feels_like" ] && show_feels_like=$(get_config "show_feels_like" || echo false)
117
118# Show UVI: "true" or "false"
119[ -z "$show_uvi" ] && show_uvi=$(get_config "show_uvi" || echo true)
120
121# Show wind: "true" or "false"
122[ -z "$show_wind" ] && show_wind=$(get_config "show_wind" || echo true)
123
124# Show humidity: "true" or "false"
125[ -z "$show_humidity" ] && show_humidity=$(get_config "show_humidity" || echo true)
126
127# Show pressure: "true" or "false"
128[ -z "$show_pressure" ] && show_pressure=$(get_config "show_pressure" || echo true)
129
130# Show daylight: "true" or "false"
131[ -z "$show_daylight" ] && show_daylight=$(get_config "show_daylight" || echo false)
132
133dateformat=$(get_config "dateformat" || echo "%a %b %d")
134timeformat=$(get_config "timeformat" || echo "%b %d %r")
135
136
137
138###[ Colors and characters ]###################################################
139
140background=$(get_config "background" || echo "\033[44m")
141text=$(get_config "text" || echo "\033[36;1m")
142data=$(get_config "data" || echo "\033[33;1m")
143delimiter=$(get_config "delimiter" || echo "\033[35m:")
144dashes=$(get_config "dashes" || echo "\033[34m-")
145
146
147
148###[ Text Labels ]#############################################################
149
150greeting_text=$(get_config "greeting_text" || echo "Weather in")
151wind_text=$(get_config "wind_text" || echo "Wind")
152feels_like_text=$(get_config "feels_like_text" || echo "Feels like")
153humidity_text=$(get_config "humidity_text" || echo "Humidity")
154pressure_text=$(get_config "pressure_text" || echo "Pressure")
155sunrise_text=$(get_config "sunrise_text" || echo "Sunrise")
156sunset_text=$(get_config "sunset_text" || echo "Sunset")
157forecast_text=$(get_config "forecast_text" || echo "forecast")
158
159
160
161###[ Unicode Symbols for icons ]###############################################
162
163sun=$(get_config "sun" || echo "\033[33;1m\xe2\x98\x80")
164moon=$(get_config "moon" || echo "\033[36m\xe2\x98\xbd")
165clouds=$(get_config "clouds" || echo "\033[37;1m\xe2\x98\x81")
166rain=$(get_config "rain" || echo "\033[37;1m\xe2\x98\x94")
167fog=$(get_config "fog" || echo "\033[37;1m\xe2\x96\x92")
168mist=$(get_config "mist" || echo "\033[34m\xe2\x96\x91")
169haze=$(get_config "haze" || echo "\033[33m\xe2\x96\x91")
170snow=$(get_config "snow" || echo "\033[37;1m\xe2\x9d\x84")
171thunderstorm=$(get_config "thunderstorm" || echo "\033[33;1m\xe2\x9a\xa1")
172
173
174
175###[ Fetch Weather data ]######################################################
176
177api_cmd=$([ "$forecast" != 0 ] && echo "forecast/daily" || echo "weather")
178
179if [ "$location" -gt 0 ] 2> /dev/null
180then
181	# Location is all numeric
182	weather=$($fetch_cmd "https://api.openweathermap.org/data/2.5/$api_cmd?id=$location&units=$units&appid=$api_key")
183else
184	# Location is a string
185	location=$(echo "$location" | sed "s/ /%20/g")
186	weather=$($fetch_cmd "https://api.openweathermap.org/data/2.5/$api_cmd?q=$location&units=$units&appid=$api_key")
187fi
188
189if [ -z "$weather" ]
190then
191	echo "ERROR: Cannot fetch weather data"
192	exit 75 # EX_TEMPFAIL
193fi
194
195status_code=$(echo "$weather" | jq -r '.cod' 2>/dev/null)
196
197if [ "$status_code" != 200 ]
198then
199	echo "ERROR: Cannot fetch weather data for the given location"
200	exit 69 # EX_UNAVAILABLE
201fi
202
203
204
205###[ Fetch UV data ]###########################################################
206
207if [ "$show_uvi" = true ] && [ "$forecast" = 0 ]
208then
209	lat=$(echo "$weather" | jq -r '.coord.lat')
210	lon=$(echo "$weather" | jq -r '.coord.lon')
211
212	uvdata=$($fetch_cmd "https://api.openweathermap.org/data/2.5/uvi?lat=$lat&lon=$lon&appid=$api_key")
213	uvi=$(echo "$uvdata" | jq -r '.value')
214fi
215
216
217
218###[ Process Weather data ]####################################################
219
220epoch_to_date() {
221	if date -j -r "$1" +"%a %b %d" > /dev/null 2>&1; then
222		# BSD
223		ret=$(date -j -r "$1" +"$dateformat")
224	else
225		# GNU
226		ret=$(date -d "@$1" +"$dateformat")
227	fi
228	echo "$ret"
229}
230
231if [ "$forecast" != 0 ]
232then
233	city=$(echo "$weather" | jq -r '.city.name')
234	flength=$(echo "$weather" | jq '.list | length')
235	forecast=$([ "$forecast" -gt "$flength" ] && echo "$flength" || echo "$forecast")
236else
237	city=$(echo "$weather" | jq -r '.name')
238	temperature=$(echo "$weather" | jq '.main.temp' | xargs gprintf "%.0f")
239	humidity=$(echo "$weather" | jq '.main.humidity')
240	feels_like=$(echo "$weather" | jq '.main.feels_like' | xargs printf "%.0f")
241	pressure=$(echo "$weather" | jq '.main.pressure')
242	sky=$(echo "$weather" | jq -r '.weather[0].main')
243	sunrise=$(echo "$weather" | jq '.sys.sunrise')
244	sunset=$(echo "$weather" | jq '.sys.sunset')
245	wind=$(echo "$weather" | jq '.wind.speed')
246	azimuth=$(echo "$weather" | jq '.wind.deg')
247fi
248
249
250
251###[ Process Wind data ]#######################################################
252
253set -- $(get_config "wind_directions" || echo "N NNE NE ENE E ESE SE SSE S SSW SW WSW W WNW NW NNW")
254
255if [ "$forecast" = 0 ]
256then
257	shift "$(echo "scale=0; ($azimuth + 11.25)/22.5 % 16" | bc)"
258	direction=$1
259fi
260
261
262
263###[ Process Sunrise and Sunset data ]#########################################
264
265epoch_to_time() {
266	if date -j -r "$1" +"%r" > /dev/null 2>&1; then
267		# BSD
268		ret=$(date -j -r "$1" +"$timeformat")
269	else
270		# GNU
271		ret=$(date -d "@$1" +"$timeformat")
272	fi
273	echo "$ret"
274}
275
276if [ "$forecast" = 0 ]
277then
278	if [ -n "$sunrise" ]
279	then
280		sunrise_time=$(epoch_to_time "$sunrise")
281	fi
282
283	if [ -n "$sunset" ]
284	then
285		sunset_time=$(epoch_to_time "$sunset")
286	fi
287fi
288
289
290
291###[ Set the period ]##########################################################
292
293now=$(date +%s)
294
295if [ "$forecast" != 0 ]
296then
297	period="none"
298else
299	if [ -z "$sunset" ] || [ -z "$sunrise" ]
300	then
301		period="day"
302	elif [ "$now" -ge "$sunset" ] || [ "$now" -le "$sunrise" ]
303	then
304		period="night"
305	else
306		period="day"
307	fi
308fi
309
310
311
312###[ Set the scale ]###########################################################
313
314case $units in
315	metric)
316		scale="°C"
317		speed_unit="m/s"
318		pressure_unit="hPa"
319		pressure=$(echo "$pressure" | xargs gprintf "%.0f")
320		;;
321	imperial)
322		scale="°F"
323		speed_unit="mph"
324		pressure_unit="inHg"
325		if [ "$forecast" = 0 ]
326		then
327			pressure=$(echo "$pressure*0.0295" | bc | xargs gprintf "%.2f")
328		fi
329		;;
330esac
331
332
333
334###[ Set icons ]###############################################################
335
336get_icon() {
337	case $1 in
338		Clear)
339			if [ $period = "night" ]
340			then
341				echo "$moon "
342			else
343				echo "$sun "
344			fi
345			;;
346		Clouds)
347			echo "$clouds "
348			;;
349		Rain)
350			echo "$rain "
351			;;
352		Fog)
353			echo "$fog "
354			;;
355		Mist)
356			echo "$mist "
357			;;
358		Haze)
359			echo "$haze "
360			;;
361		Snow)
362			echo "$snow "
363			;;
364		Thunderstorm)
365			echo "$thunderstorm "
366			;;
367	esac
368}
369
370
371
372###[ Display current Weather ]#################################################
373
374if [ "$forecast" != 0 ]
375then
376	output="$background$text $city $forecast_text$text$delimiter "
377
378	i=0
379	while [ $i -lt "$forecast" ]
380	do
381		day=$(echo "$weather" | jq ".list[$i]")
382		date=$(epoch_to_date "$(echo "$day" | jq -r '.dt')")
383		low=$(echo "$day" | jq -r '.temp.min' | xargs gprintf "%.0f")
384		high=$(echo "$day" | jq -r '.temp.max' | xargs gprintf "%.0f")
385
386		icon=""
387		if [ "$symbols" = true ]
388		then
389			sky=$(echo "$day" | jq -r '.weather[0].main')
390			icon=$(get_icon "$sky")
391		fi
392
393		output="$output$text$date$delimiter $data$high$text/$data$low $scale $icon"
394		if [ $i -lt $((forecast-1)) ]
395		then
396			output="$output$dashes "
397		fi
398
399		i=$((i + 1))
400	done
401else
402	if [ "$symbols" = true ]
403	then
404		icon="$(get_icon "$sky")"
405	fi
406	output="$background$text $greeting_text $city$delimiter$data $temperature $scale $icon"
407
408        if [ "$show_feels_like" = true ]
409        then
410                output="$output$dashes$text $feels_like_text$delimiter$data $feels_like $scale "
411        fi
412
413	if [ "$show_uvi" = true ]
414	then
415		output="$output$dashes$text UVI$delimiter$data $uvi "
416	fi
417
418	if [ "$show_wind" = true ]
419	then
420		output="$output$dashes$text $wind_text$delimiter$data $wind $speed_unit $direction "
421	fi
422
423	if [ "$show_humidity" = true ]
424	then
425		output="$output$dashes$text $humidity_text$delimiter$data $humidity%% "
426	fi
427
428	if [ "$show_pressure" = true ]
429	then
430		output="$output$dashes$text $pressure_text$delimiter$data $pressure $pressure_unit "
431	fi
432
433	if [ "$show_daylight" = true ]
434	then
435		output="$output$dashes$text $sunrise_text$delimiter$data $sunrise_time $dashes$text $sunset_text$delimiter$data $sunset_time "
436	fi
437fi
438
439if [ "$ansi" = true ]
440then
441	env gprintf "$output\033[0m\n"
442else
443	env gprintf "$output\n" | sed "s/$(gprintf '\033')\[[0-9;]*m//g"
444fi
445