This guide briefly explains the steps I took to build a weather app
for
the Debian(Trixie) operating system command line using only R.
suppressPackageStartupMessages({
library(crayon)
library(cli)
library(httr2)
library(knitr)
})
I used the API from https://open-meteo.com/en/docs as the data source.
The site provides many options and data parameters than can be returned
when
making an API call. I specified the GFS model and parameters included
temperature,
relative humidity, precipitation probability, wind speed, and wind
direction.
I created a request object using httr2::request() and then performed
the
request with httr2::req_perform(). The data is returned in JSON format
so
httr2::resp_body_json is used to parse the data.
url <- "https://api.open-meteo.com/v1/forecast?latitude=42.7325&longitude=-84.5555&daily=temperature_2m_max,temperature_2m_min,precipitation_sum,precipitation_probability_max,wind_direction_10m_dominant&models=gfs_seamless¤t=temperature_2m,relative_humidity_2m,precipitation,wind_direction_10m,wind_speed_10m&timezone=America%2FNew_York&wind_speed_unit=mph&precipitation_unit=inch&temperature_unit=fahrenheit"
response <- request(url) %>% req_perform()
data <- resp_body_json(resp = response)
Using print(data) I can now see what was returned by the API call.
This panel is pretty straightforward. I’m using cli::cli_h1 to make a
header
and crayon::bold() and crayon::green(), crayon::blue(), etc. to add
text
effects.
cli_h1(bold("Currently in Lansing, MI"))
##
## ── Currently in Lansing, MI ────────────────────────────────────────────────────
cat("\n")
cat(bold("🌡️ Temperature: "), green(data$current$temperature_2m, "°F\n"),
bold("💧 Humidity: "), cyan(data$current$relative_humidity_2m, "%\n"),
bold("🌧️ Rain/Precip: "), blue(data$current$precipitation, "in\n"),
bold("💨 Wind Speed: "), yellow(data$current$wind_speed_10m, "mph\n"),
bold("Wind Direction:"), black(data$current$wind_direction_10m))
## 🌡️ Temperature: 94.1 °F
## 💧 Humidity: 49 %
## 🌧️ Rain/Precip: 0 in
## 💨 Wind Speed: 13.8 mph
## Wind Direction: 228
cat("\n")
Note: To add emojis I had to search for the ASCII unicode characters.
Press
ctrl+shift+u and then type in the 4 digit code and press enter.
This panel is a bit more involved as it requires a function. The
first
function is assigning an icon for the day based on the probability
of
precipitation.
get_precip_emoji <- function(prob){
if (prob < 20) return("☀️")
if (prob < 50) return("⛅ ")
return("🌧")}
I could then build my 7 day data frame using data.frame():
forecast_df <- data.frame(Day = as.Date.character(data$daily$time),
Icon = sapply(data$daily$precipitation_probability_max, get_precip_emoji),
High = paste0(data$daily$temperature_2m_max, "°F"),
Low = paste0(data$daily$temperature_2m_min, "°F"),
Precip_Prob = paste0(data$daily$precipitation_probability_max, "%"),
Precip_Total = paste0(data$daily$precipitation_sum, "in."),
Wind_Direction = paste0(data$daily$wind_direction_10m_dominant))
Finally, using knitr::kable() I could produce the table with
absolutely zero
formatting required.
kable(forecast_df)
| Day | Icon | High | Low | Precip_Prob | Precip_Total | Wind_Direction |
|---|---|---|---|---|---|---|
| 2026-06-30 | ☀️ | 94.3°F | 75.6°F | 2% | 0in. | 215 |
| 2026-07-01 | ☀️ | 97.4°F | 75.8°F | 6% | 0in. | 231 |
| 2026-07-02 | ☀️ | 94.3°F | 78.4°F | 7% | 0in. | 242 |
| 2026-07-03 | ⛅ | 92.1°F | 74.6°F | 47% | 0.012in. | 237 |
| 2026-07-04 | ⛅ | 87.9°F | 70.7°F | 47% | 0.528in. | 249 |
| 2026-07-05 | ⛅ | 80.9°F | 62.9°F | 45% | 0.004in. | 54 |
| 2026-07-06 | ⛅ | 84.1°F | 62.4°F | 20% | 0in. | 80 |