# Finding out weather conditions from the command line

In this post, I outline an approach for retrieving weather conditions from the command line. There are websites and widgets that provide weather details but I like using the command line because I find that it's more efficient than pointing and clicking on stuff. In addition, this approach enables us to program specific tasks. For example, we can set up a task to check the weather every 30 minutes and if weather conditions are getting bad (or going to get bad as forecasted), send us a message or an email. We can program any alert that we need!

We will be using an API provided by Meteomatics to retrieve weather conditions and you need to register an account before you can use the API; you can register for a Free Basic API Package at their API page. The other requirements are R, the lubridate R package, and wget (or if you prefer, you can use curl). We don't really need R and could use some scripting language (like Bash) instead but the lubridate package makes it easy to work with dates.

After registering an account you should receive an email with your username and password, which you need to make an API request. The request URI (uniform resource identifier) follows this format:

https://api.meteomatics.com/validdatetime/parameters/locations/format?optionals

I will go through each section of the URI.

### Valid datetime

The API accepts the date and time in the format specified by ISO 8601 and in UTC. We can get the current time in R using Sys.time(); R will automatically detect your time zone but we can also explicitly state it.

To find your time zone use the function OlsonNames(), which outputs all supported time zones in R, together with grep(). It's called Olson because that's the surname of the founding contributor.

grep("Tokyo", OlsonNames(), value=TRUE)
# [1] "Asia/Tokyo"


The with_tz() function from the lubridate package allows us to output datetimes at different time zones.

with_tz(Sys.time(), "Australia/Perth")
# [1] "2022-03-17 21:10:15 AWST"

with_tz(Sys.time(), "Asia/Tokyo")
# [1] "2022-03-17 22:10:15 JST"


To generate the current time in UTC according to ISO 8601, we can use the following code; the Z is the time zone designator for UTC time.

paste0(
format_ISO8601(
with_tz(Sys.time(), "UTC")
),
"Z"
)
# [1] "2022-03-17T13:11:01Z"


Now that we have a valid datetime, we can move on to setting up the parameters.

### Parameters

The Free Basic API Package allows you to retrieve the following weather conditions:

API parameter Description
wind_speed_10m Instantaneous wind speed at 10m above ground
wind_dir_10m:d Instantaneous wind direction at 10m above ground in degrees
wind_gusts_10m_1h Wind gusts in 10 m in the previous 1h
wind_gusts_10m_24h Wind gusts in 10 m in the previous 24h
t_2m Instantaneous temperature at 2m above ground
t_max_2m_24h Maximum temperature at 2m height in the previous 24h
t_min_2m_24h Minimum temperature at 2m height in the previous 24h
msl_pressure Mean sea level pressure
precip_1h Precipitation accumulated over the past hour
precip_24h:mm Precipitation accumulated over the past 24 hours
weather_symbol_1h Weather symbol giving an overall impression of the weather state of the past hour
weather_symbol_24h Weather symbol giving an overall impression of the weather state of the past 24 hours
uv UV index
sunrise Sunrise
sunset Sunset

Wind direction is in the direction from which the wind is blowing; a southerly wind blows from the south to the north. The direction is given in degrees clockwise from due north, so a southerly wind has a wind direction of 180 degrees. This is very useful to know if you cycle!

The API parameter is followed by a colon (":") and the desired unit, for example Celsius for temperature. The basic package only allows up to ten parameters per query and I have gone ahead and prepared the parameter part of the URI. The trailing forward slash ("/") at the end of the string is used to delimit each part of the URI, when I join each part back up.

my_param <- 't_2m:C,weather_symbol_1h:idx,weather_symbol_24h:idx,wind_speed_10m:ms,wind_dir_10m:d,uv:idx,msl_pressure:Pa,precip_24h:mm,sunrise:sql,sunset:sql/'


Note that there are units called idx and sql, which I don't think are actual units but perhaps specify how the data is stored (in an index and SQL database).

### Locations

The free package only allows one location per query and the location is specified by GPS coordinates. The easiest way to get GPS coordinates for a location of interest is to load up Google Maps, right click on the wanted location, and then copy the coordinates to the clipboard.

my_coord <- '35.550041057153614,139.78659223114846/'


### Format

For the format, we simply want CSV.

my_format <- 'csv'


### Optionals

In this section we can specify the forecasting model to be used and I will use the mixed model, which is recommended.

my_optional <- '?model=mix'


### API call

We can now construct the API call by putting it all together and make the request using wget by using a system call in R. Please substitute my_user and my_password with your username and password. Also note that I am adding 10 hours to my_wanted_Time to get a forecast 10 hours from now and I have set my_tz to Asia/Tokyo; please change these values as you wish.

my_add_hour <- 10
my_tz <- "Asia/Tokyo"
my_user <- 'not_me'
my_url <- 'https://api.meteomatics.com/'
my_datetime <- paste0(
format_ISO8601(
with_tz(my_wanted_time, "UTC")
),
"Z/"
)

my_param <- 't_2m:C,weather_symbol_1h:idx,weather_symbol_24h:idx,wind_speed_10m:ms,wind_dir_10m:d,uv:idx,msl_pressure:Pa,precip_24h:mm,sunrise:sql,sunset:sql/'
my_coord <- '35.550041057153614,139.78659223114846/'

my_format <- 'csv'
my_optional <- '?model=mix'

paste0(
'wget --quiet -O - --user ',
my_user,
' ',
my_url,
my_datetime,
my_param,
my_coord,
my_format,
my_optional
) -> my_call

my_return <- system(my_call, intern = TRUE)


The return values are saved in my_return but they are a bit hard to read.

my_return
# [1] "validdate;t_2m:C;weather_symbol_1h:idx;weather_symbol_24h:idx;wind_speed_10m:ms;wind_dir_10m:d;uv:idx;msl_pressure:Pa;precip_24h:mm;sunrise:sql;sunset:sql"
# [2] "2022-03-17T23:34:49Z;7.0;4;8;8.7;34.8;0;101957;7.01;2022-03-16T20:49:00Z;2022-03-17T08:48:00Z"


In order to create a better output, let's first create a vector of the parameters and the units. The code below splits the long return string by their delimiters.

my_cols <- unlist(strsplit(my_return[1],';'))
my_cols <- my_cols[-1]
my_units <- sapply(my_cols, function(x) strsplit(x, ':')[[1]][2])
my_cols <- sapply(my_cols, function(x) strsplit(x, ':')[[1]][1])
my_cols
#                 t_2m:C  weather_symbol_1h:idx weather_symbol_24h:idx      wind_speed_10m:ms
#                 "t_2m"    "weather_symbol_1h"   "weather_symbol_24h"       "wind_speed_10m"
#         wind_dir_10m:d                 uv:idx        msl_pressure:Pa          precip_24h:mm
#         "wind_dir_10m"                   "uv"         "msl_pressure"           "precip_24h"
#            sunrise:sql             sunset:sql
#              "sunrise"               "sunset"


We'll create a vector of the returned values joined with their respective unit (and getting rid of the sql and idx units).

my_values <- unlist(strsplit(my_return[2],';'))
my_values <- my_values[-1]
my_values <- paste0(my_values, my_units)
my_values <- sub("sql|idx", "", my_values)
my_values
#  [1] "7.0C"                 "4"                    "8"
#  [4] "8.7ms"                "34.8d"                "0"
#  [7] "101957Pa"             "7.01mm"               "2022-03-16T20:49:00Z"
# [10] "2022-03-17T08:48:00Z"


We'll create a lookup for the weather codes, instead of displaying an uninformative number, and convert the numbers into their descriptions.

my_code <- c(
"Could not be determined",
"Clear sky",
"Light clouds",
"Partly cloudy",
"Cloudy",
"Rain",
"Rain and snow / sleet",
"Snow",
"Rain shower",
"Snow shower",
"Sleet shower",
"Light Fog",
"Dense fog",
"Freezing rain",
"Thunderstorms",
"Drizzle",
"Sandstorm"
)

names(my_code) <- as.character(0:16)

my_index <- grep("weather_symbol", my_cols)

convert_weather_symbol <- function(x){
x <- as.numeric(x)
if(x >= 100){
x <- x - 100
}
my_code[as.character(x)]
}

my_values[my_index] <- sapply(my_values[my_index], convert_weather_symbol)
my_values
#  [1] "7.0C"                 "Cloudy"               "Rain shower"
#  [4] "8.7ms"                "34.8d"                "0"
#  [7] "101957Pa"             "7.01mm"               "2022-03-16T20:49:00Z"
# [10] "2022-03-17T08:48:00Z"


We'll also convert the sunrise and sunset times back into our timezone instead of using UTC time.

my_index <- grep("^sun", my_cols)

convert_datetime <- function(x, tz = my_tz){
x <- sub("T", " ", x)
x <- sub("Z", "", x)
x <- ymd_hms(x, tz = "UTC")
format_ISO8601(with_tz(x, tz), usetz = TRUE)
}

my_values[my_index] <- sapply(my_values[my_index], convert_datetime)
my_values
#  [1] "7.0C"                     "Cloudy"                   "Rain shower"
#  [4] "8.7ms"                    "34.8d"                    "0"
#  [7] "101957Pa"                 "7.01mm"                   "2022-03-17T05:49:00+0900"
# [10] "2022-03-17T17:48:00+0900"


Finally, instead of displaying the short parameter IDs, we'll create a lookup so that we display their full description.

my_param_lookup <- c(
"Instantaneous wind speed at 10m above ground",
"Instantaneous wind direction at 10m above ground in degrees",
"Wind gusts in 10 m in the previous 1h",
"Wind gusts in 10 m in the previous 24h",
"Instantaneous temperature at 2m above ground",
"Maximum temperature at 2m height in the previous 24h",
"Minimum temperature at 2m height in the previous 24h",
"Mean sea level pressure",
"Precipitation accumulated over the past hour",
"Precipitation accumulated over the past 24 hours",
"Weather symbol of the weather state of the past hour",
"Weather symbol of the weather state of the past 24 hours",
"UV index",
"Sunrise",
"Sunset"
)

names(my_param_lookup) <- c(
"wind_speed_10m",
"wind_dir_10m",
"wind_gusts_10m_1h",
"wind_gusts_10m_24h",
"t_2m",
"t_max_2m_24h",
"t_min_2m_24h",
"msl_pressure",
"precip_1h",
"precip_24h",
"weather_symbol_1h",
"weather_symbol_24h",
"uv",
"sunrise",
"sunset"
)

rbind(
c("Time", format_ISO8601(with_tz(my_wanted_time, tzone = my_tz), usetz = TRUE)),
data.frame(variable = my_param_lookup[my_cols], value = my_values, row.names = NULL)
)
#                                                       variable                    value
# 1                                                         Time 2022-03-18T08:34:49+0900
# 2                 Instantaneous temperature at 2m above ground                     7.0C
# 3         Weather symbol of the weather state of the past hour                   Cloudy
# 4     Weather symbol of the weather state of the past 24 hours              Rain shower
# 5                 Instantaneous wind speed at 10m above ground                    8.7ms
# 6  Instantaneous wind direction at 10m above ground in degrees                    34.8d
# 7                                                     UV index                        0
# 8                                      Mean sea level pressure                 101957Pa
# 9             Precipitation accumulated over the past 24 hours                   7.01mm
# 10                                                     Sunrise 2022-03-17T05:49:00+0900
# 11                                                      Sunset 2022-03-17T17:48:00+0900


### Weather info from the command line

We can save all the code above into a R script and call it from the command line to retrieve the weather conditions.

#!/usr/bin/env Rscript

suppressPackageStartupMessages(library(lubridate))

args <- commandArgs(trailingOnly=TRUE)

if (length(args) > 0){
message("Please enter the number of hours to forecast")
quit(status = 1)
}
}

my_tz <- "Asia/Tokyo"
my_user <- 'not_me'
my_url <- 'https://api.meteomatics.com/'
my_datetime <- paste0(
format_ISO8601(
with_tz(my_wanted_time, "UTC")
),
"Z/"
)

my_param <- 't_2m:C,weather_symbol_1h:idx,weather_symbol_24h:idx,wind_speed_10m:ms,wind_dir_10m:d,uv:idx,msl_pressure:Pa,precip_24h:mm,sunrise:sql,sunset:sql/'
my_coord <- '35.550041057153614,139.78659223114846/'
my_format <- 'csv'
my_optional <- '?model=mix'

paste0(
'wget --quiet -O - --user ',
my_user,
' ',
my_url,
my_datetime,
my_param,
my_coord,
my_format,
my_optional
) -> my_call

my_return <- system(my_call, intern = TRUE)

my_param_lookup <- c("Wind speed at 10m above ground",
"wind direction at 10m above ground",
"Wind gusts in 10 m in the previous 1h",
"Wind gusts in 10 m in the previous 24h",
"Temperature at 2m above ground",
"Maximum temperature at 2m in the previous 24h",
"Minimum temperature at 2m in the previous 24h",
"Mean sea level pressure",
"Precipitation over the past hour",
"Precipitation over the past 24 hours",
"Weather symbol of the past hour",
"Weather symbol of the past 24 hours",
"UV index",
"Sunrise",
"Sunset"
)

names(my_param_lookup) <- c(
"wind_speed_10m",
"wind_dir_10m",
"wind_gusts_10m_1h",
"wind_gusts_10m_24h",
"t_2m",
"t_max_2m_24h",
"t_min_2m_24h",
"msl_pressure",
"precip_1h",
"precip_24h",
"weather_symbol_1h",
"weather_symbol_24h",
"uv",
"sunrise",
"sunset"
)

my_code <- c(
"Could not be determined",
"Clear sky",
"Light clouds",
"Partly cloudy",
"Cloudy",
"Rain",
"Rain and snow / sleet",
"Snow",
"Rain shower",
"Snow shower",
"Sleet shower",
"Light Fog",
"Dense fog",
"Freezing rain",
"Thunderstorms",
"Drizzle",
"Sandstorm"
)

names(my_code) <- as.character(0:16)

my_cols <- unlist(strsplit(my_return[1],';'))
my_cols <- my_cols[-1]
my_units <- sapply(my_cols, function(x) strsplit(x, ':')[[1]][2])
my_cols <- sapply(my_cols, function(x) strsplit(x, ':')[[1]][1])

my_values <- unlist(strsplit(my_return[2],';'))
my_values <- my_values[-1]
my_values <- paste0(my_values, my_units)
my_values <- sub("sql|idx", "", my_values)

my_index <- grep("weather_symbol", my_cols)
convert_weather_symbol <- function(x){
x <- as.numeric(x)
if(x >= 100){
x <- x - 100
}
my_code[as.character(x)]
}
my_values[my_index] <- sapply(my_values[my_index], convert_weather_symbol)

my_index <- grep("^sun", my_cols)
convert_datetime <- function(x, tz = my_tz){
x <- sub("T", " ", x)
x <- sub("Z", "", x)
x <- ymd_hms(x, tz = "UTC")
format_ISO8601(with_tz(x, tz), usetz = TRUE)
}
my_values[my_index] <- sapply(my_values[my_index], convert_datetime)

rbind(
c("Current time", format_ISO8601(with_tz(my_wanted_time, tzone = my_tz), usetz = TRUE)),
data.frame(variable = my_param_lookup[my_cols], value = my_values, row.names = NULL)
)


I have named the script get_weather.R, which you execute to get the weather info.

Current weather.

./get_weather.R
variable                    value
1                          Current time 2022-03-17T22:55:57+0900
2        Temperature at 2m above ground                    12.2C
3       Weather symbol of the past hour                     Rain
4   Weather symbol of the past 24 hours              Rain shower
5        Wind speed at 10m above ground                    6.7ms
6    wind direction at 10m above ground                    63.3d
7                              UV index                        0
8               Mean sea level pressure                 101909Pa
9  Precipitation over the past 24 hours                   2.44mm
10                              Sunrise 2022-03-17T05:49:00+0900
11                               Sunset 2022-03-17T17:48:00+0900


Weather seven hours from now.

./get_weather.R 7
variable                    value
1                          Current time 2022-03-18T05:56:43+0900
2        Temperature at 2m above ground                     7.6C
3       Weather symbol of the past hour                   Cloudy
4   Weather symbol of the past 24 hours              Rain shower
5        Wind speed at 10m above ground                    8.6ms
6    wind direction at 10m above ground                    43.4d
7                              UV index                        0
8               Mean sea level pressure                 101982Pa
9  Precipitation over the past 24 hours                   7.42mm
10                              Sunrise 2022-03-17T05:49:00+0900
11                               Sunset 2022-03-17T17:48:00+0900


And there you have it, finding out weather conditions from the command line!