Skip to content
Adam Mladý edited this page Oct 9, 2025 · 3 revisions

Introduction

This page will show you how the API parser works, how the OpenMeteo API works and how do functions which directly use the API work.

API Parser

The API Parser is a function that expects five arguments, that being latitude, longitude, city, country and units. The city and country are not needed really, but when refactoring it made sense, since the return value is an associative array.

Units are needed as I wanted to include both imperial and metric units.

  case $UNITS in
  imp)
    TEMP_U="&temperature_unit=fahrenheit"
    WIND_U="&wind_speed_unit=mph"
    PREC_U="&precipitation_unit=inch"
    ;;
  met)
    TEMP_U=""
    WIND_U=""
    PREC_U=""
    ;;
  esac

Here's the code for parsing units. I selected Fahrenheit, mph (wind) and inch (rain) for the imperial units. Metric units may be left empty as OpenMeteoAPI defaults to metric.

The API URL is quite long so I will split it up.

WTHR_API=$(curl -s "https://api.open-meteo.com/v1/forecast?\
latitude=$LAT&longitude=$LONG&current=temperature_2m,relative_humidity_2m,\
precipitation,cloud_cover,wind_speed_10m,wind_direction_10m,wind_gusts_10m,\
is_day,weather_code&timezone=auto$TEMP_U$WIND_U$PREC_U" | jq -c)

It expects Latitude and Longitude and optionally Units. wayweather is getting the Temperature, Humidity, Precipitation, Cloud Cover, Wind Speed, Gusts and Direction, Day or Night and Weather Code. The last two ones are for the NerdFonts icon.

Parsing of the response is between the lines 28-34 in src/meteo_api and will not be explained here, as it's basic jq and awk parsing.

Wind Direction is parsed into letters via:

WIND_ARR=(
    "North"
    "North East"
    "East"
    "South East"
    "South"
    "South West"
    "West"
    "North West"
  )
  WIND_DIR="$(echo "$WTHR_API" | jq -r '.current.wind_direction_10m')"
  WIND_DIR_LET="${WIND_ARR[$(echo "$WIND_DIR" | awk '{print int(($1 / 45) % 8 )}')]}"

The calculation is not my creation, as I found it on a code sharing website.

The Icons are an Associative array of weather codes from openmeteo and corresponding weather icons from NerdFonts. The code is on line 54 in src/meteo_api.

The last part is adding all elements into an array.

  WTHR=(
    ["CITY"]=$CITY
    ["COUNTRY"]=$COUNTRY
    ["TIME"]=$TIME
    ["TEMP"]=$TEMP
    ["HUMID"]=$HUMID
    ["PREC"]=$PREC
    ["CLOUD"]=$CLOUD
    ["WIND_S"]=$WIND_S
    ["WIND_G"]=$WIND_G
    ["WIND_DIR"]="$WIND_DIR_LET ($WIND_DIR°)"
    ["WI"]=$WI
  )

  printf "%s " "${WTHR[@]@K}"

The return code was copied from some nice user on stackoverflow, as it keeps the array assiociative.

--get or Waybar Get, --print or Human Get

The help message for --get is

> wayweather [-g/--get] [--no-icon]

OPTIONS:
  --no-icon    Return a waybar parsable
               json string withou a NerdFont
               icon

Returns a compact JSON string for the waybar
module. It is not human readable.
Use with a restart interval.

EXAMPLE:
  > wayweather --get
  {"text":"71.1°F <big>󰖔 </big>","tooltip":"New York, USA\nTime: 2025-09-30 19:45\n\n
  Temperature: 71.1°F\nHumidity: 53%\nPrecipitaion: 0.00 inch\nCloud Cover: 19%\n
  Wind Direct: North West (356°)\nWind Speed: 6.3 mph\nWind Gusts: 15.2 mph"}

Both --get and --print support disabling the icon if you do not want it. You will not see the icon between <big> </big> as you need a NerdFont Patched font.

The help from --print is

> wayweather [-p/--print] [--no-icon]

OPTIONS:
  --no-icon       Disables the weather icon
                  from NerdFonts

Prints the waybar result to stdout in a human
readable format.
For testing purposes

This one does not have much information in it, just some basics.

The code that results in the Waybar JSON is

  ICON="$(if $NO_ICON; then echo ""; else echo " <big>${WTHR_ARR["WI"]}</big>"; fi)"

  echo "{'text': '${WTHR_ARR["TEMP"]}$ICON'\
,'tooltip': '${WTHR_ARR["CITY"]}, ${WTHR_ARR["COUNTRY"]}\
\nTime: ${WTHR_ARR["TIME"]}\n\nTemperature: ${WTHR_ARR["TEMP"]}\
\nHumidity: ${WTHR_ARR["HUMID"]}\nPrecipitaion: ${WTHR_ARR["PREC"]}\
\nCloud Cover: ${WTHR_ARR["CLOUD"]}\nWind Direct: ${WTHR_ARR["WIND_DIR"]}\
\nWind Speed: ${WTHR_ARR["WIND_S"]}\nWind Gusts: ${WTHR_ARR["WIND_G"]}'}" | sed -e "s/'/\"/g" | jq -c

Other that boolean based if statement for the icon, the only fun thing is the sed command (I didn't want to \" the double quotes)

The result for --print is much more readable

echo "TEXT:
${WTHR_ARR["TEMP"]}$ICON

TOOLTIP:
${WTHR_ARR["CITY"]}, ${WTHR_ARR["COUNTRY"]}
Time: ${WTHR_ARR["TIME"]}

Temperature: ${WTHR_ARR["TEMP"]}
Humidity: ${WTHR_ARR["HUMID"]}
Precipitaion: ${WTHR_ARR["PREC"]}
Cloud Cover: ${WTHR_ARR["CLOUD"]}
Wind Direct: ${WTHR_ARR["WIND_DIR"]}
Wind Speed: ${WTHR_ARR["WIND_S"]}
Wind Gusts: ${WTHR_ARR["WIND_G"]}"

The Text is what is visible in Waybar and Tooltip when hovering the module.

Daemon

The daemon is a looping system printing waybar json on config change.

> wayweather [-w/--daemon] [OPTIONS]

OPTIONS:
  --no-icon       Disables the weather icon
                  from NerdFonts
  --location=NUM  Expects a location ID from --list
                  Will load the location into
                  config and use it
  --timer=NUM     Expects the amount of seconds
                  the daemon should wait before
                  checking changes in config
                  Default: 15, Recommended: 5 - 30

Runs a loop with the same components as --get but
checks changes in the config so the API is not
polled every time.
The check is done in three parts, so that is has
redundancy and has a 15 minute timer for API
updates.
Returns a stream of waybar parsable JSON strings.

You can change the default location via an ID from --load and change the config checking timer.

The location check is just an integer checker with existence checker, that later run load with the ID

    location=*)
      LOCATION="${i#*=}"
      if [[ "$LOCATION" =~ ^[0-9]+$ ]]; then
        ID=false
        for key in $(cat "$SAVE_PATH" | jq -rc 'keys | @sh' | tr -d \'); do
          if [[ "$LOCATION" -eq "$key" ]]; then
            ID=true
          fi
        done
        if $ID; then
          load_loc "$LOCATION"
        fi
      fi

The checker uses three step process, so that it catches the config change every time. It is just a few comparisons in a config history array. There is also a loop checking if the next quarter hour has passed for api pulling.

    unset HISTORY
    declare -A HISTORY
    HISTORY["0"]="NONE"
    HISTORY["1"]="NONE"
    HISTORY["2"]="NONE"
    UPDATE=false

    .......

    while true; do
      HISTORY["0"]="$(cat "$CONF_PATH")"
      if [[ "${HISTORY["1"]}" != "NONE" && "${HISTORY["2"]}" != "NONE" ]] && [[ "${HISTORY["0"]}" != "${HISTORY["1"]}" || "${HISTORY["0"]}" != "${HISTORY["2"]}" || "${HISTORY["1"]}" != "${HISTORY["2"]}" ]]; then
        break
      fi
      sleep "$((SLEEP / 2))s"
      HISTORY["1"]="$(cat "$CONF_PATH")"
      if [[ "${HISTORY["2"]}" != "NONE" ]] && [[ "${HISTORY["0"]}" != "${HISTORY["1"]}" || "${HISTORY["0"]}" != "${HISTORY["2"]}" || "${HISTORY["1"]}" != "${HISTORY["2"]}" ]]; then
        break
      fi
      sleep "$((SLEEP / 2))s"
      HISTORY["2"]="$(cat "$CONF_PATH")"
      if [[ "${HISTORY["0"]}" != "${HISTORY["1"]}" || "${HISTORY["0"]}" != "${HISTORY["2"]}" || "${HISTORY["1"]}" != "${HISTORY["2"]}" ]]; then
        break
      fi
      for i in $(seq 0 4); do
        if [[ "$(($(date +"%M") - $((15 * i))))" == "0" ]]; then
          UPDATE=true
        fi
      done
      if $UPDATE; then
        break
      fi
    done

Clone this wiki locally