/posts/i3blocks-air-quality-module2019/07/17

i3blocks Air Quality Module

2019/07/17
A simple module to keep track of air quality.
devblogair qualitycurlsedawki3blocks

Hello!

This is really old and I'm not sure if it's still relevant. I'm keeping it here for reference. (2024-09-01)

Overview

Making API requests and displaying the data on i3 status bar. Using cURL, sed, and awk to retrieve & manipulate the data. This post will cover the process of making a module for i3-blocks to display the local air quality index.

Why?

During my stay in South Korea I've noticed that air quality was a rising issue. People here are mostly blaming it on the neighboring country, but I'm not going to get into that.

Rather than fixing the bigger problem, I've decided to write a module for my status bar to keep me updated on the local air quality. I'm doing little things that I can actually achieve.

While it's technically a module, it's just a group of commands to display a number on my status bar. Nothing really new. There are already modules for this kind of task, and I knew where to get the air quality report from, so all I had to do was to put the two together.

Preparation

First I had to grab the air quality data from a website. By default, it shows your location, local air quality forecast, and explains the numbers on screen. Further down the page, there's a link to an API. However I didn't need the other information that the API provided, so I still needed to put in some effort to make the information usable for the status bar.

I needed to get a token to access the API, which was well documented on the Initial setup section of the page. After I got my token I was able to make a successful request using curl,

$ curl -s http://api.wapi.info/feed/here/?token=yourtoken

which returned the following:

{
    "status":"ok",
    "data":
    {
        "aqi":69,
        "idx":xxxx,
        "attributions":
        [
            {
                "url":"https://www.airkorea.or.kr/",
                "name":"South Air Korea Environment Corporation"
            },
            {
                "url":"https://waqi.info/",
                "name":"World Air Quality Index Project"
            }
        ],
        "city":
        {
            "geo":[latitude,longitude],
            "name":"Some City, Some Place, Somewhere, South Korea",
            "url":"https://aqicn.org/city/korea/some/place"
        },
        "time":
        {
            "s":"2019-07-10 22:00:00",
            "tz":"+09:00",
            "v":1562796000
        },
        "debug":
        {
            "sync":"2019-07-10T22:26:57+09:00"
        }
    }
}

I modified the response so it won't exactly look like that, but it's pretty standard JSON. Using here returns the local air quality, the website also contains a list of all the cities that are available.

Now What?

I saved the response to a file, which was done with pipes and redirection.

$ curl -s http://api.wapi.info/feed/here?token=mytoken > ~/file/location/airdata

Now with a file to work with, I had to trim the response because I only needed the AQI value.

I decided to use sed and awk for this text manipulation, there's a better way to do this probably, but this is how I did it.

$ sed "s/,/\n/g;s/:/ /g" ~/file/location/airdata | awk /data/ | awk "{print \$3}"

Breakdown

  • sed is a stream editor, it reads the file line by line and performs the operations that are given to it.
  • s/,/\n/g replaces all the commas with new lines.
  • s/:/ /g replaces all the colons with spaces.
  • awk /data/ prints the line that contains the word "data".
  • awk "{print \$3}" prints the third word of the line.

Output for each command:

  1. {"status" "ok" "data" {"aqi" 69
  2. "data" {"aqi" 69
  3. 69

Nice, now that I have what I need, I can move on to the i3blocks part of this.

Some Assembly Required

Luke Smith's weather module for i3blocks was a good place to start.

I had to modify the script to point to the correct directory and file, which will be created by the script later.

rm -f "$HOME/.local/share/airdata" ;}

This will clear pre-existing data file when the script runs.

I modified the getforecast() function into get_air_quality().

get_air_quality() {
    ping -q -c 1 1.1.1.1 > /dev/null || exit 1
    curl -s "http://api.wapi.info/feed/here?token=mytoken" > "$HOME/.local/share/airdata" || exit 1
    }

This will:

  1. Check if device is connected to the internet.
  2. Then send a request to the API.
  3. Save the response and create a data file.

Which will be read by the script.Then I modified the showforecast() function into show_air_quality().

show_air_quality() {
    sed "s/,/\n/g;s/:/ /g" $HOME/.local/share/airdata | awk /data/ | awk "{print \"AQI: \" \$3}"
    }

This function will return us something that looks like AQI: 69, which will be displayed on the status bar.

case $BLOCK_BUTTON in
    2) get_air_quality && show_air_quailty ;;
    3) pgrep -x dunst >/dev/null && notify-send "Air Quality Module" "\- Middle click to update forecast.
-AQI - Air Quality Index
   0-50: Good
 51-100: Moderate
101-150: Mildly Unhealthy
151-200: Unhealthy" ;;
esac

if [ "$(stat -c %y "$HOME/.local/share/airdata" >/dev/null 2>&1 | awk '{print $1}')" != "$(date '+%Y-%m-%d')" ]
	then rm $HOME/.local/share/airdata && get_air_quality && show_air_quality
	else show_air_quality
fi

Mouse Actions

Each case corresponds to different mouse actions:

  • 1) Left click
  • 2) Middle click
  • 3) Right click

For this module, I've yet to find a use for left clicking on the module, so I decided to leave it out. I added some descriptions for the numbers. Saved the file as "airquality", then added it to my i3blocks config file, then set it to update every 30 minutes.

Final Touches

Still need to make the script executable.

# chmod +x ~/.local/bin/statusbar/airquality

in my case.

Wrapping Up

Once I reloaded the i3wm, I was greeted with an air quality index. The module responds to mouse actions as expected, and I'm happy with the result.

Writing this module was a good practice, since I got to write something that I found useful, and it was pretty easy to do so since most of the work was already done for me. I got to make something I could use everyday.